【问题标题】:Json.Net - performant deserialization onto interface based data structure?Json.Net - 基于接口的数据结构的高性能反序列化?
【发布时间】:2017-07-03 09:02:33
【问题描述】:

只要我在数据结构中没有 only 具体类型,我将一个巨大的 json 树结构反序列化为,它开始使用大量内存,但它的内存占用量保持相对较小反序列化为完全具体的类型……有没有一个优雅的解决方法?

我得到的 json 是在其他地方生成的,所以我对得到它的格式没有影响(它是一个树结构,如果它直接序列化为 json,类似于下面的代码示例),并且在最坏的情况下大约250-300MB。 我用来映射它的数据结构看起来有点像下面的例子(虽然在某些地方是结构)

public class Node : INode
{
    [JsonConverter(typeof(NodeTypeConverter<IInnerNode, InnerNodeType1>))]
    public List<INodeInner> InnerNodes { get; set; }
}

public class InnerNodeNodeType1 : INode
{
    [JsonConverter(typeof(NodeTypeConverter<IInnerNode, InnerNodeType2>))]
    public List<INodeInner> InnerNodes { get; set; }

    // some other properties
}

public class InnerNodeNodeType2 : INode
{
    [JsonConverter(typeof(NodeTypeConverter<IInnerNode, InnerNodeType3>))]
    public List<INodeInner> InnerNodes { get; set; }

    // some even different properties
}

…

然而,我没有找到一种方法来映射它而不让它运行的 PC 跪下,特别是内存方面(除此之外,在某些地方 List&lt;interface&gt; 我什至没有得到 json.Net要使用转换器,它甚至在检查转换器类之前就抛出了错误Could not create an instance of type {type}. Type is an interface or abstract class and cannot be instantiated....)。

所以现在,我将其更改为所有具体类型/具体类型实例的列表,而不是接口和转换器,并且它运行时内存占用少得多(数量级!)。但这并不优雅,因为这样一来,我就不能将大多数类重用于不同种类的树,而我将不得不在程序的其他地方使用这些树,它们相似,但略有不同。

有没有一个优雅的解决方案?

PS:感谢您阅读本文!这个问题可能没有完美地提出和/或包含您可能需要提出解决方案的所有和任何类型的信息。然而,我发现,每当我试图涵盖所有基础并预测所有进一步的问题时,我都没有得到任何回应根本,所以这一次我试图提出不同的问题......:P

【问题讨论】:

  • 你能分享一下NodeTypeConverter&lt;TInterface, TConcrete&gt;的代码吗?

标签: c# json interface tree json.net


【解决方案1】:

您没有提供问题的具体示例,但您确实编写了 我将其更改为所有具体类型/具体类型实例的列表,而不是接口和转换器,并且它运行时少得多内存占用量(数量级!)。听起来好像您必须以某种中间表示形式将大块 JSON 加载到内存中,例如整个 JSON 的 string 或 public List&lt;INodeInner&gt; InnerNodes 数组的完整内容的 JArray。之后,您将中间表示转换为最终对象。

您需要做的是避免加载任何中间表示,或者如果必须这样做,则一次仅加载可能的最小 JSON 块。下面是一个示例实现:

public interface INode
{
    List<INodeInner> InnerNodes { get; set; }
}

public interface INodeInner : INode
{
}

public class Node : INode
{
    [JsonProperty(ItemConverterType = typeof(InterfaceToConcreteConverter<INodeInner, InnerNodeNodeType1>))]
    public List<INodeInner> InnerNodes { get; set; }
}

public class InnerNodeNodeType1 : INodeInner
{
    [JsonProperty(ItemConverterType = typeof(InterfaceToConcreteConverter<INodeInner, InnerNodeNodeType2>))]
    public List<INodeInner> InnerNodes { get; set; }

    // some other properties
    public int Type1Property { get; set; }
}

public class InnerNodeNodeType2 : INodeInner
{
    [JsonProperty(ItemConverterType = typeof(InterfaceToConcreteConverter<INodeInner, InnerNodeNodeType3>))]
    public List<INodeInner> InnerNodes { get; set; }

    // some even different properties
    public int Type2Property { get; set; }
}

public class InnerNodeNodeType3 : INodeInner
{
    [JsonProperty(ItemConverterType = typeof(InterfaceToConcreteConverter<INodeInner, InnerNodeNodeType3>))]
    public List<INodeInner> InnerNodes { get; set; }

    // some even different properties
    public int Type3Property { get; set; }
}

public class InterfaceToConcreteConverter<TInterface, TConcrete> : JsonConverter where TConcrete : TInterface
{
    public InterfaceToConcreteConverter()
    {
        // TConcrete should be a subtype of an abstract type, or an implementation of an interface.  If they
        // are identical an infinite recursion could result, so throw an exception.
        if (typeof(TInterface) == typeof(TConcrete))
            throw new InvalidOperationException(string.Format("typeof({0}) == typeof({1})", typeof(TInterface), typeof(TConcrete)));
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(TInterface);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize(reader, typeof(TConcrete));
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

然后,加载:

Node root;
var settings = new JsonSerializerSettings
{
    // Whatever settings you need.
};
using (var stream = File.OpenRead(fileName))
using (var textReader = new StreamReader(stream))
using (var reader = new JsonTextReader(textReader))
{
    root = JsonSerializer.CreateDefault(settings).Deserialize<Node>(reader);
}

注意事项:

  1. 我没有为整个List&lt;INodeInner&gt; InnerNodes 编写转换器并将其与[JsonConverter(typeof(NodeTypeConverter&lt;IInnerNode, InnerNodeType1&gt;))] 一起应用,而是为每个列表项创建了一个转换器并通过设置JsonPropertyAttribute.ItemConverterType 来应用它:

    [JsonProperty(ItemConverterType = typeof(InterfaceToConcreteConverter<INodeInner, InnerNodeNodeType1>))]
    public List<INodeInner> InnerNodes { get; set; }
    

    从而简化了转换器,并保证如果转换器需要将 JSON 预加载到中间 JToken 中,一次只预加载和转换一个列表项。

  2. 由于在您的示例中,INodeInner 的类型对于 INode 的每种类型都是固定的,因此甚至不需要预加载单个列表项。相反,在JsonConverter.ReadJson() 中,使用正确的具体类型直接从传入的JsonReader 反序列化:

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize(reader, typeof(TConcrete));
    }
    
  3. 如Newtonsoft Performance Tips: Optimize Memory Usage 中所述,反序列化大型 JSON 文件时,直接从流中反序列化:

    using (var stream = File.OpenRead(fileName))
    using (var textReader = new StreamReader(stream))
    using (var reader = new JsonTextReader(textReader))
    {
        root = JsonSerializer.CreateDefault(settings).Deserialize<Node>(reader);
    }
    

示例fiddle 显示此工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    • 2012-12-09
    • 1970-01-01
    相关资源
    最近更新 更多