JsonConvert.DeserializeObject<>(string) 为 $id 属性返回空值

JsonConvert.DeserializeObjectlt;gt; (string) returns null value for $id property(JsonConvert.DeserializeObjectlt;gt;(string) 为 $id 属性返回空值)

本文介绍了JsonConvert.DeserializeObject<>(string) 为 $id 属性返回空值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 System.Net.WebClient.DownloadString 下载 JSON.我收到了有效的回复:

I'm downloading the JSON using System.Net.WebClient.DownloadString. I'm getting a valid response:

{
"FormDefinition": [
    {
        "$id":"4",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Punchworks Form"
    },
    {
        "$id":"6",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Punchworks Form test second"
    },
    {
        "$id":"46",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"any_Name"
    },
    {
        "$id":"47",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Punchworks Form test second"
    },
    {
        "$id":"49",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Testing Name ??´????? ???? ACEeišuu { [ ( ~ ! @ # "
    },
    {
        "$id":"50",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"something new"
    },
    {
        "$id":"56",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Testing Name руÌÑÑкий 汉语漢語 ĄČĘėįšųū { [ ( ~ ! @ # "
    },
    {
        "$id":"57",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Test Name"
    },
    {
        "$id":"58",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 12:59:29 PM"
    },
    {
        "$id":"59",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:01:18 PM"
    },
    {
        "$id":"60",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:40:44 PM"
    },
    {
        "$id":"61",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:43:46 PM"
    },
    {
        "$id":"62",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:48:21 PM"
    },
    {
        "$id":"63",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:57:00 PM"
    },
    {
        "$id":"64",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:57:53 PM"
    },
    {
        "$id":"65",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Unique Name - 12/16/2013 1:58:46 PM"
    },
    {
        "$id":"79",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Testing Name1211"
    },
    {
        "$id":"80",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Testing Name1211"
    },
    {
        "$id":"81",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"any_nami"
    },
    {
        "$id":"90",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Test_something3"
    },
    {
        "$id":"91",
        "Class":558,
        "ClassDisplayLabel":"Punchworks",
        "Name":"Test_something4"
    }]
}

这是我的模型:

public class FormDefinitionList
{
    [JsonProperty("FormDefinition")]
    public List<FormDefinition> FormDefinitions { get; set; }
}

public class FormDefinition
{
    [JsonProperty ("$id")]
    public string Id { get; set; }

    [JsonProperty ("Class")]
    public int Class { get; set; }

    [JsonProperty ("ClassName")]
    public string ClassName { get; set; }

    [JsonProperty ("ClassDisplayLabel")]
    public string ClassDisplayLabel { get; set; }

    [JsonProperty ("Definition")]
    public string Definition { get; set; }

    [JsonProperty ("Name")]
    public string Name { get; set; }
}

当我这样做时一切正常:

Everything works when I do:

string response = "json as above";
FormDefinitionList root = JsonConvert.DeserializeObject<FormDefinitionList> (response);

除了 Id ($id) 属性始终为空.起初我试图弄清楚我从服务器返回的美元符号是否不同,但事实并非如此.我不知道从这里去哪里,有什么想法吗?

except that the Id ($id) property is always null. At first I tried to figure out if the dollar sign symbol I was getting back from the server was different, but that doesn't appear to be the case. I'm not sure where to go from here, so any ideas?

提前致谢.

注意:如果我尝试使用 JavaScriptSerializer 之类的东西进行反序列化,它可以完美运行,所以我相当确定我的模型或 JSON.net 有问题.不过可能是错的.

NOTE: If I try to deserialize with something like JavaScriptSerializer, it works perfectly, so I'm fairly sure it's something wrong with my model or with JSON.net. Could be wrong though.

推荐答案

Json.Net 通常使用 $id$ref 作为元数据来保存 JSON 中的对象引用.因此,当它看到 $id 时,它假定属性不是实际 JSON 属性集的一部分,而是一个内部标识符.因此,它不会填充对象的 Id 属性,即使您包含指示它应该的 [JsonProperty] 属性.

Json.Net normally uses $id along with $ref as metadata to preserve object references in JSON. So when it sees $id it assumes that property is not part of the actual JSON property set, but an internal identifier. Thus it does not populate the Id property on your object, even though you included a [JsonProperty] attribute indicating that it should.

更新

截至 Json.Net 版本 6.0.4,有一个新设置,您可以通过它指示反序列化程序将这些元数据"属性视为普通属性,而不是使用它们.您需要做的就是将 MetadataPropertyHandling 设置为 Ignore,然后像往常一样反序列化.

As of Json.Net version 6.0.4, there is a new setting by which you can instruct the deserializer to treat these "metadata" properties as normal properties instead of consuming them. All you need to do is set the MetadataPropertyHandling setting to Ignore and then deserialize as usual.

var settings = new JsonSerializerSettings();
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;

var obj = JsonConvert.DeserializeObject<FormDefinitionList>(json, settings);

在版本 6.0.4 之前,需要一种变通方法来解决此问题.该答案的其余部分讨论了可能的解决方法.如果您使用的是 6.0.4 或更高版本,则不需要解决方法,现在可以停止阅读.

Prior to version 6.0.4, a workaround was needed to solve this issue. The rest of this answer discusses the possible workarounds. If you are using 6.0.4 or later, you do not need the workaround and can stop reading now.

我能看到的最简单的解决方法是在反序列化之前在 JSON 上将 "$id" 字符串替换为 "id"(包括引号),正如@Carlos Coelho 建议的那样.由于您必须对每个响应都执行此操作,因此如果您走这条路,我建议您制作一个简单的辅助方法以避免代码重复,例如:

The simplest workaround I can see is to do a string replace of "$id" with "id" (including the quotes) on the JSON prior to deserializing it, as @Carlos Coelho suggested. Since you would have to do this with each response, if you go this route I would recommend making a simple helper method to avoid code duplication, e.g.:

public static T Deserialize<T>(string json)
{
    return JsonConvert.DeserializeObject<T>(json.Replace(""$id"", ""id""));
}

但是,由于您在评论中说您不太热衷于使用字符串替换的想法,因此我研究了其他选项.我确实找到了另一种可能对您有用的替代方法——自定义 JsonConverter.转换器背后的想法是,它会尝试使用 Json.Net 的内置反序列化机制来创建和填充对象(无 ID),然后从 JSON 中手动检索 $id 属性并使用它通过反射填充对象的 Id 属性.

However, since you said in your comments that you are not so keen on the idea of using a string replace, I looked into other options. I did find one other alternative that might work for you-- a custom JsonConverter. The idea behind the converter is that it would try to use Json.Net's built-in deserialization mechanisms to create and populate the object (sans ID), then manually retrieve the $id property from the JSON and use it to populate the Id property on the object via reflection.

这是转换器的代码:

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

    public override object ReadJson(JsonReader reader, Type objectType,
                           object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        object o = jo.ToObject(objectType);
        JToken id = jo["$id"];
        if (id != null)
        {
            PropertyInfo prop = objectType.GetProperty("Id");
            if (prop != null && prop.CanWrite && 
                prop.PropertyType == typeof(string))
            {
                prop.SetValue(o, id.ToString(), null);
            }
        }
        return o;
    }

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

我尝试编写转换器,使其适用于任何具有 $id 的对象——您只需要相应地更改 CanConvert 方法,以便它除了 FormDefinition 之外,对于您需要使用它的所有类型返回 true.

I tried to write the converter such that it would work for any object that has the $id -- you just need to change the CanConvert method accordingly so that it returns true for all the types that you need to use it for in addition to FormDefinition.

要使用转换器,您只需将它的一个实例传递给 DeserializeObject<T>,如下所示:

To use the converter, you just need to pass an instance of it to DeserializeObject<T> like this:

FormDefinitionList root = JsonConvert.DeserializeObject<FormDefinitionList>(
                                      json, new DollarIdPreservingConverter());

重要提示:您可能很想用 JsonConverter 属性来装饰您的类,而不是将转换器传递给 DeserializeObject 调用,但是不要这样做——它会导致转换器进入递归循环,直到堆栈溢出.(有一种方法可以让转换器使用该属性,但您必须重写 ReadJson 方法来手动创建目标对象并填充其属性,而不是调用 jo.ToObject(objectType).可行,但有点乱.)

Important note: you might be tempted to decorate your classes with a JsonConverter attribute instead of passing the converter into the DeserializeObject call, but don't do this-- it will cause the converter to go into a recursive loop until the stack overflows. (There is a way to get the converter to work with the attribute, but you would have to rewrite the ReadJson method to manually create the target object and populate its properties instead of calling jo.ToObject(objectType). It is doable, but a little more messy.)

让我知道这是否适合你.

Let me know if this works for you.

这篇关于JsonConvert.DeserializeObject&lt;&gt;(string) 为 $id 属性返回空值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:JsonConvert.DeserializeObject&lt;&gt;(string) 为 $id 属性返回空值

基础教程推荐