Newtonsoft.Json,填充字典失败

Posted

技术标签:

【中文标题】Newtonsoft.Json,填充字典失败【英文标题】:Newtonsoft.Json, Populate Dictionary failed 【发布时间】:2016-07-13 08:30:17 【问题描述】:

我通过 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);

但是发生了这个异常:

无法将字符串“$type”转换为字典键类型“System.Guid”。创建一个 TypeConverter 以从字符串转换为键类型对象。路径“$type”,第 2 行,位置 10。

在 Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateDictionary(IDictionary 字典, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, String id)

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

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

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

在 Newtonsoft.Json.JsonConvert.PopulateObject(String value, Object target, JsonSerializerSettings 设置)

我这样写 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 现在生成的字典中的第一项是:

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"

为什么?

【问题讨论】:

您需要 dspecial 设置和 Guid 吗?不妨试试JsonConvert.SerializeObject(dic); 之类的更简单的方法,看看是否能满足您的需求。 @senschen 我使用泛型类来序列化和反序列化项目中的多种类型。我项目中的其他类型需要这个设置。请阅读我的问题的最后编辑 使用字符串而不是 Guid @Fred @ZehraSubaş 在我的问题的最后编辑中,我这样做了,但是.. 【参考方案1】:

问题是您在序列化字典时指定了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 进行序列化,并且只发出多态类型的类型信息,而您的 Dictionary&lt;Guid, string&gt; 不是。

相关,在使用TypeNameHandling 时,请注意Newtonsoft docs 中的这一警告:

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

关于为什么这可能是必要的讨论,请参阅TypeNameHandling caution in Newtonsoft Json

【讨论】:

以上是关于Newtonsoft.Json,填充字典失败的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 NewtonSoft Json.Net 将 Json 字典反序列化为平面类

c# Newtonsoft.Json封装

NewtonSoft.Json

屌丝技能--转Json(Newtonsoft.Json.dll)

使用Newtonsoft.Json.dll序列化和反序列化

将 JSON 转换为字典?