2017-07-03 117 views
1

只要我沒有只有數據結構中的具體類型我反序列化一個巨大的json樹結構,它開始使用大量的內存,但是當反序列化到完全具體的類型時,它的內存佔用會保持相對較小...有沒有一個優雅的解決方法呢?Json.Net - 基於接口的數據結構的高性能反序列化?

我得到的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

你可以分享你的'NodeTypeConverter '代碼嗎? – dbc

回答

0

你不提供了一個具體的問題示例,但你確實寫了我將它改爲具體類型實例的所有具體類型實例列表,而不是接口加上一個轉換器,它運行的內存佔用少得多(數量級! )。這聽起來好像您必須將大塊JSON加載到內存中,例如整個JSON的stringpublic List<INodeInner> 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<INodeInner> InnerNodes並用[JsonConverter(typeof(NodeTypeConverter<IInnerNode, InnerNodeType1>))]應用它,我爲每個列表項目創建了一個轉換器,並通過設置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顯示此工作。