如何在 JSON.net 中控制大量异构对象的反序列化



我正在使用JSON作为中间格式从旧的专有对象数据库格式迁移数据。这些对象被输出到一个 JSON 对象数组中,每个对象都有一个初始字段,给出原始对象的类型,后跟名为 Instance 的字段,该字段具有嵌套的原始对象。

我需要将这些内容流式传输,因为可能有数十万个 - 我不能只是将整个 JSON 数组读取到内存中然后处理它。

所以 JSON 看起来像这样:

[
{
    "Type": "Foo",
    "Instance": {
        // instance of Foo type
    }
},
{
    "Type": "Bar",
    "Instance": {
        // instance of Bar type
    }
},
// tens or hundreds of thousands more objects...
]
使用 Json.NET,一次流式

处理一个数组元素、访问"Type"属性,然后将"实例"反序列化为适当类型的 .Net 对象的最佳方法是什么?

编辑:尽管关于读取大型JSON数组存在类似的问题,但该问题并未回答访问实例的细节。

整理答案

  • 反序列化 JSON 数组流,一次一个项目
  • 使用 json.net 反序列化没有类型信息的多态 json 类,

首先,假设您有一个自定义SerializationBinder(或类似的东西),它将类型名称映射到类型。

接下来,可以使用以下扩展方法枚举流式处理 JSON 数据(进入顶级数组)中的顶级对象

public static class JsonExtensions
{
    public static IEnumerable<JObject> WalkObjects(TextReader textReader)
    {
        using (JsonTextReader reader = new JsonTextReader(textReader))
        {
            while (reader.Read())
            {
                if (reader.TokenType == JsonToken.StartObject)
                {
                    JObject obj = JObject.Load(reader);
                    if (obj != null)
                    {
                        yield return obj;
                    }
                }
            }
        }
    }
}

然后,假设您有一些用于读取 JSON 数据stream,则可以流式传输 JSON 并逐个转换顶级数组元素进行处理,如下所示:

        SerializationBinder binder = new MyBinder(); // Your custom binder.
        using (var stream = GetStream(json))
        using (var reader = new StreamReader(stream, Encoding.Unicode))
        {
            var assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
            var items = from obj in JsonExtensions.WalkObjects(reader)
                        let jType = obj["Type"]
                        let jInstance = obj["Instance"]
                        where jType != null && jType.Type == JTokenType.String
                        where jInstance != null && jInstance.Type == JTokenType.Object
                        let type = binder.BindToType(assemblyName, (string)jType)
                        where type != null
                        select jInstance.ToObject(type); // Deserialize to bound type!
            foreach (var item in items)
            {
                // Handle each item.
                Debug.WriteLine(JsonConvert.SerializeObject(item));
            }
        }

最新更新