简体   繁体   English

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

[英]JsonConvert.DeserializeObject<> (string) returns null value for $id property

I'm downloading the JSON using System.Net.WebClient.DownloadString. 我正在使用System.Net.WebClient.DownloadString下载JSON。 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"
    }]
}

And here is my Model: 这是我的模型:

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);

except that the Id ($id) property is always null. Id($ id)属性始终为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? 我不确定从这里要去哪里,有什么想法吗?

Thanks in advance. 提前致谢。

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. 注意:如果我尝试使用JavaScriptSerializer之类的东西进行反序列化,则它可以完美工作,因此,我相当确定我的模型或JSON.net都存在问题。 Could be wrong though. 可能是错误的。

Json.Net normally uses $id along with $ref as metadata to preserve object references in JSON. Json.Net通常使用$id$ref作为元数据来保存JSON中的对象引用。 So when it sees $id it assumes that property is not part of the actual JSON property set, but an internal identifier. 因此,当看到$id就假定该属性不是实际JSON属性集的一部分,而是一个内部标识符。 Thus it does not populate the Id property on your object, even though you included a [JsonProperty] attribute indicating that it should. 因此,即使您包括一个[JsonProperty]属性指示该属性,它也不会填充您对象的Id属性。

UPDATE UPDATE

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. 从Json.Net版本6.0.4开始 ,有一个新设置,您可以通过该设置指示反序列化器将这些“元数据”属性视为普通属性,而不是使用它们。 All you need to do is set the MetadataPropertyHandling setting to Ignore and then deserialize as usual. 您需要做的就是将MetadataPropertyHandling设置设置为Ignore ,然后照常反序列化。

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

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

Prior to version 6.0.4, a workaround was needed to solve this issue. 在6.0.4版之前,需要一种解决方法来解决此问题。 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. 如果使用6.0.4或更高版本,则不需要解决方法,并且可以立即停止阅读。


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. 我可以看到的最简单的解决方法是在反序列化之前,在JSON上用"id" (包括引号)对"$id"进行字符串替换,如@Carlos Coelho所建议的。 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, eg: 由于您必须对每个响应进行此操作,因此,如果您采用这种方式,我建议您使用一种简单的辅助方法来避免代码重复,例如:

public static T Deserialize<T>(string json)
{
    return JsonConvert.DeserializeObject<T>(json.Replace("\"$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 . 我确实找到了另一个可能对您JsonConverter替代方法-一个自定义的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. 转换器背后的想法是,它将尝试使用Json.Net的内置反序列化机制来创建和填充对象(sans ID),然后从JSON手动检索$id属性,并使用它来填充Id属性。通过反射的对象。

Here is the code for the converter: 这是转换器的代码:

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();
    }
}

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 . 我试着写转换器使得它能够为这一有任何对象工作$id -你只需要改变CanConvert相应的方法,以便它返回你需要另外用它为所有类型的真FormDefinition

To use the converter, you just need to pass an instance of it to DeserializeObject<T> like this: 要使用该转换器,只需要将其实例传递给DeserializeObject<T>如下所示:

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

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

Let me know if this works for you. 让我知道这是否适合您。

The problem is the $ sign, therefore a workaround would be: 问题是$符号,因此解决方法是:

Remove the $ from the JsonProperty annotation. 从JsonProperty批注中删除$。

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

On your code, replace the special character $ 在代码上,替换特殊字符$

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

Edited as @BrianRogers suggests 根据@BrianRogers的建议进行编辑

This answer fixed the $id/$ref problem for me: Json.Net adding $id to EF objects despite setting PreserveReferencesHandling to "None" 这个答案为我解决了$ id / $ ref问题: 尽管将PreserveReferencesHandling设置为“ None”,Json.Net仍将$ id添加到EF对象中。

In your implementation of DefaultContractResolver/IContractResolver, add this; 在您的DefaultContractResolver / IContractResolver的实现中,添加此代码;

public override JsonContract ResolveContract(Type type) {
    var contract = base.ResolveContract(type);
    contract.IsReference = false;
    return contract;
}

EDIT: This will remove the $id. 编辑:这将删除$ id。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM