【问题标题】:Deserializing large json from WebService using Json.NET使用 Json.NET 从 WebService 反序列化大型 json
【发布时间】:2021-05-19 17:33:42
【问题描述】:

我从 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;
    }

【问题讨论】:

    标签: c# json web-services json.net


    【解决方案1】:

    可以采用Issues parsing a 1GB json file using JSON.NETDeserialize json array stream one item at a time的基本方式,即通过JSON进行流式处理,反序列化并yield返回每个对象;但另外应用一些状态过滤表达式仅反序列化与路径 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();
    }
    

    演示小提琴here.

    【讨论】:

    • 感谢您的帮助。不幸的是,我在使用 StreamReader 时遇到了错误。看起来参数与任何构造函数都不匹配。 "使用 (var sr = new StreamReader(stream, leaveOpen: leaveOpen))"
    • 好的,即使没有“leaveOpen : leaveOpen”,它也能正常工作。所以再次感谢:)
    • @Spiky05 - 不客气。如果您的问题得到解答,请联系mark it as such
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-22
    • 1970-01-01
    相关资源
    最近更新 更多