使用 Json.NET 从 WebService 反序列化大型 json
Deserializing large json from WebService using Json.NET
我从 WebService 收到一个大的 JSON 字符串,我正在寻找使用 C# 反序列化它的最佳内存优化方式。
JSON结构:
{
"d": {
"results": [
{
"metadata": {
"id": "",
"uri": "",
"type": ""
},
"ID": "",
"Value1": "",
"Value2": "",
"Value3": ""
},
{
"metadata": {
"id": "",
"uri": "",
"type": ""
},
"ID": "",
"Value1": "",
"Value2": "",
"Value3": ""
},
]
}
}
我想获得“结果”数组中的所有对象,但只是一个接一个对象,而不是现在的完整列表。
我已经在使用 StreamReader 来避免将完整的 json 字符串加载到内存中。
是否有任何选项可以只读取一个对象,进行一些处理然后读取下一个对象以避免“OutOfMemoryExceptions”?
WebResponse response = r.GetResponse();
using (Stream dataStream = response.GetResponseStream())
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(dataStream))
using (var jsonTextReader = new JsonTextReader(sr))
{
return ((RootObject)serializer.Deserialize<RootObject>(jsonTextReader)).RootObject2.Results;
}
你可以做的是采用 and Deserialize json array stream one item at a time的基本方法,即流过JSON并反序列化并产生return 每个对象;但另外应用一些有状态的过滤表达式来反序列化仅匹配路径 d.results[*]
.
的 StartObject
个标记
为此,首先定义如下接口和扩展方法:
public interface IJsonReaderFilter
{
public bool ShouldDeserializeToken(JsonReader reader);
}
public static class JsonExtensions
{
public static IEnumerable<T> DeserializeSelectedTokens<T>(Stream stream, IJsonReaderFilter filter, JsonSerializerSettings settings = null, bool leaveOpen = false)
{
using (var sr = new StreamReader(stream, leaveOpen : leaveOpen))
using (var reader = new JsonTextReader(sr))
foreach (var item in DeserializeSelectedTokens<T>(reader, filter, settings))
yield return item;
}
public static IEnumerable<T> DeserializeSelectedTokens<T>(JsonReader reader, IJsonReaderFilter filter, JsonSerializerSettings settings = null)
{
var serializer = JsonSerializer.CreateDefault(settings);
while (reader.Read())
if (filter.ShouldDeserializeToken(reader))
yield return serializer.Deserialize<T>(reader);
}
}
现在,要仅过滤与路径 d.results[*]
匹配的项目,请定义以下过滤器:
class ResultsFilter : IJsonReaderFilter
{
const string path = "d.results";
const int pathDepth = 2;
bool inArray = false;
public bool ShouldDeserializeToken(JsonReader reader)
{
if (!inArray && reader.Depth == pathDepth && reader.TokenType == JsonToken.StartArray && string.Equals(reader.Path, "d.results", StringComparison.OrdinalIgnoreCase))
{
inArray = true;
return false;
}
else if (inArray && reader.Depth == pathDepth + 1 && reader.TokenType == JsonToken.StartObject)
return true;
else if (inArray && reader.Depth == pathDepth && reader.TokenType == JsonToken.EndArray)
{
inArray = false;
return false;
}
else
{
return false;
}
}
}
接下来,为每个结果创建以下数据模型:
public class Metadata
{
public string id { get; set; }
public string uri { get; set; }
public string type { get; set; }
}
public class Result
{
public Metadata metadata { get; set; }
public string ID { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
现在您可以按如下方式逐步反序列化您的 JSON 流:
foreach (var result in JsonExtensions.DeserializeSelectedTokens<Result>(dataStream, new ResultsFilter()))
{
// Process each result in some manner.
result.Dump();
}
演示 fiddle here.
我从 WebService 收到一个大的 JSON 字符串,我正在寻找使用 C# 反序列化它的最佳内存优化方式。
JSON结构:
{
"d": {
"results": [
{
"metadata": {
"id": "",
"uri": "",
"type": ""
},
"ID": "",
"Value1": "",
"Value2": "",
"Value3": ""
},
{
"metadata": {
"id": "",
"uri": "",
"type": ""
},
"ID": "",
"Value1": "",
"Value2": "",
"Value3": ""
},
]
}
}
我想获得“结果”数组中的所有对象,但只是一个接一个对象,而不是现在的完整列表。 我已经在使用 StreamReader 来避免将完整的 json 字符串加载到内存中。 是否有任何选项可以只读取一个对象,进行一些处理然后读取下一个对象以避免“OutOfMemoryExceptions”?
WebResponse response = r.GetResponse();
using (Stream dataStream = response.GetResponseStream())
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(dataStream))
using (var jsonTextReader = new JsonTextReader(sr))
{
return ((RootObject)serializer.Deserialize<RootObject>(jsonTextReader)).RootObject2.Results;
}
你可以做的是采用d.results[*]
.
StartObject
个标记
为此,首先定义如下接口和扩展方法:
public interface IJsonReaderFilter
{
public bool ShouldDeserializeToken(JsonReader reader);
}
public static class JsonExtensions
{
public static IEnumerable<T> DeserializeSelectedTokens<T>(Stream stream, IJsonReaderFilter filter, JsonSerializerSettings settings = null, bool leaveOpen = false)
{
using (var sr = new StreamReader(stream, leaveOpen : leaveOpen))
using (var reader = new JsonTextReader(sr))
foreach (var item in DeserializeSelectedTokens<T>(reader, filter, settings))
yield return item;
}
public static IEnumerable<T> DeserializeSelectedTokens<T>(JsonReader reader, IJsonReaderFilter filter, JsonSerializerSettings settings = null)
{
var serializer = JsonSerializer.CreateDefault(settings);
while (reader.Read())
if (filter.ShouldDeserializeToken(reader))
yield return serializer.Deserialize<T>(reader);
}
}
现在,要仅过滤与路径 d.results[*]
匹配的项目,请定义以下过滤器:
class ResultsFilter : IJsonReaderFilter
{
const string path = "d.results";
const int pathDepth = 2;
bool inArray = false;
public bool ShouldDeserializeToken(JsonReader reader)
{
if (!inArray && reader.Depth == pathDepth && reader.TokenType == JsonToken.StartArray && string.Equals(reader.Path, "d.results", StringComparison.OrdinalIgnoreCase))
{
inArray = true;
return false;
}
else if (inArray && reader.Depth == pathDepth + 1 && reader.TokenType == JsonToken.StartObject)
return true;
else if (inArray && reader.Depth == pathDepth && reader.TokenType == JsonToken.EndArray)
{
inArray = false;
return false;
}
else
{
return false;
}
}
}
接下来,为每个结果创建以下数据模型:
public class Metadata
{
public string id { get; set; }
public string uri { get; set; }
public string type { get; set; }
}
public class Result
{
public Metadata metadata { get; set; }
public string ID { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
}
现在您可以按如下方式逐步反序列化您的 JSON 流:
foreach (var result in JsonExtensions.DeserializeSelectedTokens<Result>(dataStream, new ResultsFilter()))
{
// Process each result in some manner.
result.Dump();
}
演示 fiddle here.