2016-01-20 58 views
2

我目前使用json.net來反序列化一箇中等大小的對象集合的字符串。總共約7000件。字符串在序列化程序上的實例。反序列化<T>()

每個項目有4名相同的字符串重複組,在內存分析這造成約40000引用取決於嵌套等。

有沒有辦法讓串行用於每個相同的字符串相同的參考?

例JSON:

[{ 
    "name":"jon bones", 
    "groups":[{ 
     "groupName":"Region", 
     "code":"1" 
    },{ 
     "groupName":"Class", 
     "code":"4" 
    }] 
}, 
{ 
    "name":"Swan moans", 
    "groups":[{ 
     "groupName":"Region", 
     "code":"12" 
    },{ 
     "groupName":"Class", 
     "code":"1" 
    }] 
}] 

加入實施例。正如你所看到的,groupName值幾乎可以在所有對象上重複。只是相關的代碼改變。這不是一個很大的問題,但隨着數據集的增長,我寧願不增加分配太多。

它也可能看起來像「代碼」可能重複,但它是每個人都是獨一無二的。基本上是同一個對象的多個標識符。

+1

這是否真的是你的應用程序的瓶頸? 40,000個參考文獻聽起來不多。 – Habib

+0

這篇關於JSON.NET 8的博客文章可能會引起您的興趣(儘管不是您的問題的直接答案):http://james.newtonking.com/archive/2015/12/20/json-net-8-0- release-1-allocation-and-bug-fixes – CodingGorilla

+0

@Habib,這實際上不是問題,內存佔用可以忽略不計,但如果內存分配較少,我仍然會更喜歡它,爲什麼40000有四個! – BastanteCaro

回答

4

如果你事先知道你的4名標準的字符串,你可以用String.Intern()實習生(或直接聲明爲字符串文字的地方 - 即不工作),然後使用下面的custom JsonConverter所有JSON字符串文字轉換爲他們的實習值,如果找到一個:

 var settings = new JsonSerializerSettings { Converters = new [] { new InternedStringConverter() } }; 
     var root = JsonConvert.DeserializeObject<RootObject>(jsonString, settings); 

你也可以將它應用於使用JsonPropertyAttribute.ItemConverterType特定的字符串集合:

public class InternedStringConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(string); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 
     var s = reader.TokenType == JsonToken.String ? (string)reader.Value : (string)JToken.Load(reader); // Check is in case the value is a non-string literal such as an integer. 
     return String.IsInterned(s) ?? s; 
    } 

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

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

這可以通過串行設置全局應用:

public class Group 
{ 
    [JsonProperty(ItemConverterType = typeof(InternedStringConverter))] 
    public List<string> StandardStrings { get; set; } 
} 

如果你不提前知道的四根弦,你可以創建一個實習生串轉換器因爲他們正在閱讀:

public class AutoInterningStringConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     // CanConvert is not called when a converter is applied directly to a property. 
     throw new NotImplementedException("AutoInterningStringConverter should not be used globally"); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 
     var s = reader.TokenType == JsonToken.String ? (string)reader.Value : (string)JToken.Load(reader); // Check is in case the value is a non-string literal such as an integer. 
     return String.Intern(s); 
    } 

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

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

不過,我強烈反對全球使用這因爲你可能最終將大量的字符串添加到內部字符串表中。相反,只有它適用於特定字符串集合(S),你有信心含有少量的唯一字符串的副本:

public class Group 
{ 
    [JsonProperty(ItemConverterType = typeof(AutoInterningStringConverter))] 
    public List<string> StandardStrings { get; set; } 
} 

更新

從你更新的問題,我看你具有標準值的字符串屬性,而不是具有標準值的字符串集合。因此,你應該使用每個[JsonConverter(typeof(AutoInterningStringConverter))]

public class Group 
{ 
    [JsonConverter(typeof(AutoInterningStringConverter))] 
    public string groupName { get; set; } 

    public string code { get; set; } 
} 
+0

這完美的工作..謝謝。 – BastanteCaro

0

在其他的答案中指出,你必須要非常小心,因爲分配的一生的使用中的String.intern。對於一小部分經常使用的字符串,這可能是合適的。

對於我們的場景,我選擇遵循.Net中XML序列化器的模式。他們使用類調用「System.Xml.NameTable」來解析XML文檔中字符串的唯一出現。我遵循上面'dbc'提供的實現模式,但使用NameTable而不是String。實習生

public class JsonNameTable 
    : System.Xml.NameTable 
{ 
} 

public class JsonNameTableConverter 
    : JsonConverter 
{ 
    private JsonNameTable _nameTable; 
    public JsonNameTableConverter(JsonNameTable nameTable) 
    { 
     _nameTable = nameTable; 
    } 

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     if (reader.TokenType == JsonToken.Null) 
      return null; 
     var s = reader.TokenType == JsonToken.String ? (string)reader.Value : (string)Newtonsoft.Json.Linq.JToken.Load(reader); // Check is in case the value is a non-string literal such as an integer. 
     if (s != null) 
     { 
      s = _nameTable.Add(s); 
     } 
     return s; 
    } 

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

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

然後在使用代碼,設置轉換到JSON的設置

JsonNameTable nameTable = new JsonNameTable(); 
settings.Converters.Add(new JsonNameTableConverter(nameTable)); 

這可以讓你分享的字符串,並與該JsonNameTable參考對照琴絃的壽命。

這裏可能有一個改進:NameTable實際上會返回給定char [],開始和結束索引的現有字符串。在字符串從流中讀取時,可能會進一步降低名稱表一級,從而繞過甚至任何重複字符串的創建。然而,我不知道如何在Json.Net