我正在调用一个API,它将响应作为JSON对象返回。JSON对象的一个成员可以有一个 really long(10 MiB到3GiB+)base-64编码值。举例来说:
{
"name0": "value0",
"name1": "value1",
"data": "(very very long base-64 value here)",
"name2": "value2"
}
字符串
我需要数据和其他名称/值从身体。我如何获得这些数据?
我目前正在使用Newtonsoft.json在这个应用程序中序列化JSON数据,对于较小的数据块,我通常会有一个byte[]
类型的Data
属性,但这个数据可能超过2GiB,即使它小于这个值,也可能会有太多的响应返回,以至于我们可能会耗尽内存。
我希望有一种方法可以编写一个自定义的JsonConverter
或其他东西来将数据序列化/反序列化为System.IO.Stream
,但我不确定如何读取一个本身无法放入内存的字符串“令牌”。有什么建议吗?
1条答案
按热度按时间ghg1uchk1#
3GiB+字符串值太大,不适合.NET字符串,因为它将超过maximum .NET string length。因此,您不能使用Json.NET读取JSON响应,因为Json.NET的
JsonTextReader
在读取even when skipping then时总是完全物化属性值。至于反序列化为
Stream
或byte []
数组,如Panagiotis Kanavos的注解中所述JSON.NET的JsonTextReader和System.Text.Json的Utf8 JsonReader都没有将节点作为流检索的方法。所有与字节相关的方法一次返回全部内容。
因此,对于足够大的
data
值,您将超过maximum .NET array length。你有什么选择
其次,您可以尝试将代码从this answer通过mtosh推广到 * Parsing a JSON file with .NET core 3.0/System.text.Json *。这个答案展示了如何使用System.Text.JSON中的
Utf8JsonReader
逐个令牌地迭代流。您可以尝试重写该答案,以支持增量阅读单个字符串值-但是,我必须承认,我不知道Utf8JsonReader
是否真的支持在不加载整个值的情况下以块的形式读取属性值的一部分。因此,我不能推荐这种方法。第三,您可以采用this answer到 * JsonConvert Deserialize Object out of memory exception * 的方式,使用
JsonReaderWriterFactory.CreateJsonReader()
返回的reader手动解析您的JSON。这个工厂返回一个XmlDictionaryReader
,它动态地将JSON代码转换为XML,因此支持通过XmlReader.ReadContentAsBase64(Byte[], Int32, Int32)
增量阅读Base64属性。这是WCF的DataContractJsonSerializer
使用的读取器,不建议用于新的开发,但已移植到.NET Core,因此可以在没有其他选项时使用。那么,这将如何工作?首先定义一个与JSON对应的模型,如下所示,将
Data
属性表示为Stream
:字符串
接下来,定义以下扩展方法:
型
现在你可以反序列化你的模型如下:
型
备注:
data
的值可以任意大,因此无法将其内容反序列化为MemoryStream
。替代方案包括:FileStream
,例如由File.Create(Path.GetTempFileName(), BufferSize, FileOptions.DeleteOnClose)
返回。Microsoft.IO.RecyclableMemoryStream
nuget包返回的RecyclableMemoryStream
。上面的演示代码使用
RecyclableMemoryStream
,但如果您愿意,可以将其更改为使用FileStream
。无论哪种方式,你都需要在完成后处理它。Stream
的属性,我使用DataContractJsonSerializer
来反序列化它们的值。这个序列化器有很多怪癖,比如一个时髦的默认DateTime
,所以你可能需要尝试一下你的DataContractJsonSerializerSettings
,或者手动反序列化某些属性。JsonReaderWriterExtensions.DeserializeModelWithStreams()
只支持根级别的Stream
值属性。如果你嵌套了巨大的Base64值属性,你将需要重写JsonReaderWriterExtensions.PopulateModelWithStreams()
以使其递归(这基本上相当于编写你自己的序列化器)。JsonReaderWriterFactory
返回的读取器如何将JSON转换为XML的讨论,请参阅 * Efficiently replacing properties of a large JSON using System.Text.Json * 和 * Mapping Between JSON and XML *。演示小提琴here。