Newtonsoft.Json,Populate Dictionary失败

时间:2016-03-25 14:59:42

标签: c# json dictionary serialization json.net

我通过Newtonsoft.json将字典序列化为json,并且代码如下:

var serializeSettings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,
            Formatting = Formatting.Indented
        };
        var serializedObject = JsonConvert.SerializeObject(dic, serializeSettings);

这段代码生成一个像这样的json:

{
  "$type": "System.Collections.Generic.Dictionary`2[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
  "9648af76-7986-4b34-8b2c-97b2345769ef": "Test"
}

我尝试通过以下代码将json反序列化为字典:

var newDic = new Dictionay<Guid,string>();
var deserializeSettings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,
    Formatting = Formatting.Indented
}
JsonConvert.PopulateObject(serializedObject, newDic, deserializeSettings);

但是会发生这种异常:

  

无法转换字符串&#39; $ type&#39;字典键类型&#39; System.Guid&#39;。创建TypeConverter以将字符串转换为键类型对象。路径&#39; $ type&#39;,第2行,第10位。

     

at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateDictionary(IDictionary dictionary,JsonReader reader,JsonDictionaryContract contract,JsonProperty containerProperty,String id)

     

at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Populate(JsonReader reader,Object target)

     

at Newtonsoft.Json.JsonSerializer.PopulateInternal(JsonReader reader,Object target)

     

at Newtonsoft.Json.JsonSerializer.Populate(JsonReader reader,Object target)

     

at Newtonsoft.Json.JsonConvert.PopulateObject(String value,Object target,JsonSerializerSettings settings)

我像这样写GuidConverter并使用它。但没有工作

public class GuidConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(Guid));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return serializer.Deserialize<Guid>(reader);
        }
        catch
        {
            return Guid.Empty;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

修改

我发现了我的问题。更改代码以将json反序列化为Dictionary&lt; string,string&gt;现在生成的字典中的第一项是:

Kay: "$type"
Value : "System.Collections.Generic.Dictionary`2[[System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

为什么?

1 个答案:

答案 0 :(得分:4)

问题是您在序列化词典时指定了TypeNameHandling = TypeNameHandling.All。这会导致元数据"$type"属性作为字典中的第一个对象发出:

{
  "$type": "System.Collections.Generic.Dictionary`2[[System.Guid, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
  "9648af76-7986-4b34-8b2c-97b2345769ef": "Test"
}

使用DeserializeObject进行反序列化时,当构造相应的c#对象时,此标记通常由Json.NET使用。但是您在预先分配的字典上使用PopulateObject。因此,在构造期间不会消耗元数据属性,而是Json.NET尝试将其添加到字典中,并且失败。

解决方案是在deserializeSettings中设置MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead。这样做会导致无条件地使用或忽略"$type"属性(视情况而定):

var deserializeSettings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    TypeNameAssemblyFormat = FormatterAssemblyStyle.Full,
    Formatting = Formatting.Indented,
    MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
};
JsonConvert.PopulateObject(serializedObject, newDic, deserializeSettings);

请注意,使用此设置时,release notes的内存使用量和速度会有轻微的成本。

或者,如果您无法在JSON中无条件地需要元数据类型信息,则可以使用TypeNameHandling = TypeNameHandling.Auto进行序列化,并仅发出多态类型的类型信息,您的{{{ 1}}不是。

相关,在使用Dictionary<Guid, string>时,请注意Newtonsoft docs中的这一注意事项:

  

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。使用非None以外的值进行反序列化时,应使用自定义SerializationBinder验证传入类型。

有关可能需要执行此操作的讨论,请参阅 TypeNameHandling caution in Newtonsoft Json

相关问题