2016-01-20 75 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