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

its*_*ohn 8 c# webclient json.net

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

{
"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"
    }]
}
Run Code Online (Sandbox Code Playgroud)

这是我的模型:

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; }
}
Run Code Online (Sandbox Code Playgroud)

我做的每件事都有效:

string response = "json as above";
FormDefinitionList root = JsonConvert.DeserializeObject<FormDefinitionList> (response);
Run Code Online (Sandbox Code Playgroud)

除了Id($ id)属性始终为null.起初我试图弄清楚我从服务器回来的美元符号是不同的,但事实并非如此.我不知道从哪里开始,所以任何想法?

提前致谢.

注意:如果我尝试使用JavaScriptSerializer之类的反序列化,它可以完美地工作,所以我很确定我的模型或JSON.net有问题.可能是错的.

Bri*_*ers 12

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

UPDATE

从Json.Net版本6.0.4开始,有一个新设置可以指示反序列化器将这些"元数据"属性视为普通属性而不是消耗它们.您需要做的就是将MetadataPropertyHandling设置设置为Ignore,然后照常反序列化.

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

var obj = JsonConvert.DeserializeObject<FormDefinitionList>(json, settings);
Run Code Online (Sandbox Code Playgroud)

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


我可以看到的最简单的解决方法是在反序列化之前在JSON上"$id"用字符串替换"id"(包括引号),正如@Carlos Coelho建议的那样.由于你必须对每个响应都这样做,如果你走这条路线,我建议你做一个简单的帮助方法来避免代码重复,例如:

public static T Deserialize<T>(string json)
{
    return JsonConvert.DeserializeObject<T>(json.Replace("\"$id\"", "\"id\""));
}
Run Code Online (Sandbox Code Playgroud)

但是,既然你在评论中说过你不是那么热衷于使用字符串替换的想法,那么我调查了其他选项.我找到了另一个可能适合你的替代方案 - 一个习惯JsonConverter.转换器背后的想法是它会尝试使用Json.Net的内置反序列化机制来创建和填充对象(无ID),然后$id从JSON 手动检索属性并使用它来填充Id对象上的属性反射.

这是转换器的代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

FormDefinitionList root = JsonConvert.DeserializeObject<FormDefinitionList>(
                                      json, new DollarIdPreservingConverter());
Run Code Online (Sandbox Code Playgroud)

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

如果这对您有用,请告诉我.