一旦我在数据结构中没有只具体类型我将一个巨大的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<interface>
的一些地方,我甚至没有得到json .Net使用转换器,它甚至在检查转换器类之前就抛出了错误Could not create an instance of type {type}. Type is an interface or abstract class and cannot be instantiated.
。)。
所以现在,我将它更改为具体类型/具体类型实例列表而不是接口和转换器,并且它运行的内存占用量少(数量级!)。但它不够优雅,因为这样,我不能将大多数类重用于不同类型的树,我将不得不在程序的其他地方使用它们,这些树类似,但略有不同。
对此有优雅的解决方案吗?
PS:感谢您阅读这篇文章!这个问题可能没有完美提出和/或包含建议解决方案可能需要的所有和任何类型的信息。然而,我发现,无论何时我试图涵盖所有基础并预测所有进一步的问题,我都没有得到的答案,所以这次是我尝试不同的问题...:P
答案 0 :(得分:0)
您没有提供问题的具体示例,但您确实写了我将其更改为具体类型/具体类型实例列表而不是接口和转换器,并且它运行时更少的内存占用(数量级!)。听起来好像你必须在一些中间表示中将大块JSON加载到内存中,例如整个JSON的string
或JArray
数组的完整内容public List<INodeInner> InnerNodes
。之后,您将中间表示转换为最终对象。
您需要做的是避免加载任何中间表示,或者如果必须这样做,请一次只加载尽可能小的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);
}
注意:
我没有为整个List<INodeInner> InnerNodes
编写转换器并将其应用于[JsonConverter(typeof(NodeTypeConverter<IInnerNode, InnerNodeType1>))]
,而是为每个列表项创建了一个转换器,并通过设置JsonPropertyAttribute.ItemConverterType
来应用它:
[JsonProperty(ItemConverterType = typeof(InterfaceToConcreteConverter<INodeInner, InnerNodeNodeType1>))]
public List<INodeInner> InnerNodes { get; set; }
因此简化了转换器并保证,如果转换器需要将JSON预加载到中间JToken
,则只预加载一个列表项并立即转换。
因为在您的示例中,INodeInner
的类型对于每种类型的INode
都是固定的,所以甚至不需要预加载单个列表项。相反,在JsonConverter.ReadJson()
中,使用正确的具体类型直接从传入的JsonReader
反序列化:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize(reader, typeof(TConcrete));
}
如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显示此工作。