C# 使用Newtonsoft反序列化JSON时缓存/插入对象键
我有一个API方法,它将一个DB中约100k行加载到内存中,每一行都包含一个JSON字符串。对于每个请求,JSON对象结构在所有行中都是相同的(相同的对象键),尽管我不会提前知道这一点 目前,我正在为每一行调用C# 使用Newtonsoft反序列化JSON时缓存/插入对象键,c#,json,string,json.net,deserialization,C#,Json,String,Json.net,Deserialization,我有一个API方法,它将一个DB中约100k行加载到内存中,每一行都包含一个JSON字符串。对于每个请求,JSON对象结构在所有行中都是相同的(相同的对象键),尽管我不会提前知道这一点 目前,我正在为每一行调用JObject.Parse(row.Json),以获得一个JObject。当我检查堆时,我可以看到每个对象键字符串的重复条目。因此,如果我在每一行的JSON中都有对象键id,并且我有100k行,那么我在内存中会看到这个字符串的100k个实例 我想缓存这些对象键(或者可能是String.In
JObject.Parse(row.Json)
,以获得一个JObject。当我检查堆时,我可以看到每个对象键字符串的重复条目。因此,如果我在每一行的JSON中都有对象键id
,并且我有100k行,那么我在内存中会看到这个字符串的100k个实例
我想缓存这些对象键(或者可能是String.Intern()
,具体取决于生命周期),并重用这些JObject
s中的字符串。我可以看到,使用JsonConvert.DeserializeObject()
我可以提供一个自定义转换器,但是它们允许您修改JSON值,而不是键
注意:我必须一次将所有100k行存储在内存中,因为我稍后会运行一个algo,该algo需要同时执行所有操作。如果您知道JSON的结构,您可以创建一个包含最常见字段的类。这将节省相当多的空间
class RowData
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("anyOtherFixedField")]
public string OtherField{ get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> ExtraProperties {get; set;}
}
类行数据
{
[JsonProperty(“id”)]
公共int Id{get;set;}
[JsonProperty(“任何其他固定字段”)]
公共字符串OtherField{get;set;}
[JsonExtensionData]
公共IDictionary外部属性{get;set;}
}
具有属性的字段在堆上根本没有字符串
JSON中没有相应属性的任何字段都将进入
ExtraProperties
字典。如果您知道JSON的结构,您可以创建一个包含最常见字段的类。这将节省相当多的空间
class RowData
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("anyOtherFixedField")]
public string OtherField{ get; set; }
[JsonExtensionData]
public IDictionary<string, JToken> ExtraProperties {get; set;}
}
类行数据
{
[JsonProperty(“id”)]
公共int Id{get;set;}
[JsonProperty(“任何其他固定字段”)]
公共字符串OtherField{get;set;}
[JsonExtensionData]
公共IDictionary外部属性{get;set;}
}
具有属性的字段在堆上根本没有字符串
JSON中没有相应属性的任何字段都将进入
ExtraProperties
字典。似乎没有一个好方法可以钩住默认的JObject反序列化
基于提供的,我制作了一个自定义转换器,它创建了一个JValue
/JObject
/JArray
,而不是ExpandoObject
。在转换器的生命周期内,所有对象键都被缓存和重用
要使用此转换器,必须指定要反序列化为JToken
、JObject
或JArray
。
如果未指定目标类型,则不会使用此转换器
var data = JsonConvert.DeserializeObject<JToken>(json, new NameCachingJObjectConverter());
var data=JsonConvert.DeserializeObject(json,新名称cachingjObjectConverter());
以及实施
public class NameCachingJObjectConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// can write is set to false
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return ReadValue(reader);
}
private JToken ReadValue(JsonReader reader)
{
if (!MoveToContent(reader))
{
throw new Exception("Unexpected end of content");
}
switch (reader.TokenType)
{
case JsonToken.StartObject:
return ReadObject(reader);
case JsonToken.StartArray:
return ReadList(reader);
default:
if (IsPrimitiveToken(reader.TokenType))
{
return new JValue(reader.Value);
}
throw new Exception("Unexpected token when converting object: {reader.TokenType}");
}
}
private static bool IsPrimitiveToken(JsonToken token)
{
switch (token)
{
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Undefined:
case JsonToken.Null:
case JsonToken.Date:
case JsonToken.Bytes:
return true;
default:
return false;
}
}
private static bool MoveToContent(JsonReader reader)
{
JsonToken t = reader.TokenType;
while (t == JsonToken.None || t == JsonToken.Comment)
{
if (!reader.Read())
{
return false;
}
t = reader.TokenType;
}
return true;
}
private JArray ReadList(JsonReader reader)
{
var list = new JArray();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
default:
object v = ReadValue(reader);
list.Add(v);
break;
case JsonToken.EndArray:
return list;
}
}
throw new Exception("Unexpected end when reading JObject.");
}
private JToken ReadObject(JsonReader reader)
{
var expandoObject = new JObject();
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
string propertyName = GetCachedName(reader.Value.ToString());
if (!reader.Read())
{
throw new Exception("Unexpected end when reading JObject.");
}
var v = ReadValue(reader);
expandoObject[propertyName] = v;
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
return expandoObject;
}
}
throw new Exception("Unexpected end when reading ExpandoObject.");
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return (typeof(JToken).IsAssignableFrom(objectType));
}
/// <summary>
/// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite => false;
private string GetCachedName(string value)
{
string ret;
if (!cache.TryGetValue(value, out ret))
{
cache[value] = value;
ret = value;
}
return ret;
}
private readonly Dictionary<string, string> cache = new Dictionary<string, string>();
}
公共类名称cachingjobjectconverter:JsonConverter
{
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
//can write设置为false
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
返回ReadValue(reader);
}
私有JToken ReadValue(JsonReader)
{
如果(!移动内容(读卡器))
{
抛出新异常(“内容意外结束”);
}
开关(reader.TokenType)
{
案例JsonToken.StartObject:
返回ReadObject(reader);
案例JsonToken.StartArray:
返回ReadList(reader);
违约:
if(IsPrimitiveToken(reader.TokenType))
{
返回新的JValue(reader.Value);
}
抛出新异常(“转换对象时出现意外标记:{reader.TokenType}”);
}
}
专用静态布尔IsPrimitiveToken(JsonToken令牌)
{
交换机(令牌)
{
案例JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
案例JsonToken.Boolean:
案例JsonToken。未定义:
案例JsonToken.Null:
案例JsonToken。日期:
案例JsonToken.Bytes:
返回true;
违约:
返回false;
}
}
私有静态布尔移动内容(JsonReader)
{
JsonToken t=reader.TokenType;
while(t==JsonToken.None | | t==JsonToken.Comment)
{
如果(!reader.Read())
{
返回false;
}
t=reader.TokenType;
}
返回true;
}
私有JArray读取列表(JsonReader)
{
var list=new JArray();
while(reader.Read())
{
开关(reader.TokenType)
{
案例JsonToken。评论:
打破
违约:
对象v=读取值(读取器);
增加(五)项;
打破
案例JsonToken.EndArray:
退货清单;
}
}
抛出新异常(“读取JObject时意外结束”);
}
私有JToken ReadObject(JsonReader)
{
var expandoObject=new JObject();
while(reader.Read())
{
开关(reader.TokenType)
{
案例JsonToken.PropertyName:
string propertyName=GetCachedName(reader.Value.ToString());
如果(!reader.Read())
{
抛出新异常(“读取JObject时意外结束”);
}
var v=读取值(读取器);
扩展对象[属性]