使用JSON.NET将JSON数据反序列化为C#

147

我对使用C#和JSON数据相对较新,并正在寻求指导。我的环境是C# 3.0,.NET3.5SP1和JSON.NET 3.5r6。

我有一个已定义的C#类,需要从JSON结构中填充它。然而,并非每个从Web服务检索到的条目的JSON结构都包含在C#类中定义的所有可能属性。

我一直在以似乎是错误且困难的方式进行,只是逐个选择JObject中的每个值,并将字符串转换为所需的类属性。

JsonSerializer serializer = new JsonSerializer();
var o = (JObject)serializer.Deserialize(myjsondata);

MyAccount.EmployeeID = (string)o["employeeid"][0];

如何将JSON结构反序列化为C#类,并处理可能缺少的JSON源数据?

我的类定义如下:

  public class MyAccount
  {

    [JsonProperty(PropertyName = "username")]
    public string UserID { get; set; }

    [JsonProperty(PropertyName = "givenname")]
    public string GivenName { get; set; }

    [JsonProperty(PropertyName = "sn")]
    public string Surname { get; set; }

    [JsonProperty(PropertyName = "passwordexpired")]
    public DateTime PasswordExpire { get; set; }

    [JsonProperty(PropertyName = "primaryaffiliation")]
    public string PrimaryAffiliation { get; set; }

    [JsonProperty(PropertyName = "affiliation")]
    public string[] Affiliation { get; set; }

    [JsonProperty(PropertyName = "affiliationstatus")]
    public string AffiliationStatus { get; set; }

    [JsonProperty(PropertyName = "affiliationmodifytimestamp")]
    public DateTime AffiliationLastModified { get; set; }

    [JsonProperty(PropertyName = "employeeid")]
    public string EmployeeID { get; set; }

    [JsonProperty(PropertyName = "accountstatus")]
    public string AccountStatus { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpiration")]
    public DateTime AccountStatusExpiration { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpmaxdate")]
    public DateTime AccountStatusExpirationMaxDate { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifytimestamp")]
    public DateTime AccountStatusModified { get; set; }

    [JsonProperty(PropertyName = "accountstatusexpnotice")]
    public string AccountStatusExpNotice { get; set; }

    [JsonProperty(PropertyName = "accountstatusmodifiedby")]
    public Dictionary<DateTime, string> AccountStatusModifiedBy { get; set; }

    [JsonProperty(PropertyName = "entrycreatedate")]
    public DateTime EntryCreatedate { get; set; }

    [JsonProperty(PropertyName = "entrydeactivationdate")]
    public DateTime EntryDeactivationDate { get; set; }

  }

以下是需要解析的JSON示例:

{
    "givenname": [
        "Robert"
    ],
    "passwordexpired": "20091031041550Z",
    "accountstatus": [
        "active"
    ],
    "accountstatusexpiration": [
        "20100612000000Z"
    ],
    "accountstatusexpmaxdate": [
        "20110410000000Z"
    ],
    "accountstatusmodifiedby": {
        "20100214173242Z": "tdecker",
        "20100304003242Z": "jsmith",
        "20100324103242Z": "jsmith",
        "20100325000005Z": "rjones",
        "20100326210634Z": "jsmith",
        "20100326211130Z": "jsmith"
    },
    "accountstatusmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliation": [
        "Employee",
        "Contractor",
        "Staff"
    ],
    "affiliationmodifytimestamp": [
        "20100312001213Z"
    ],
    "affiliationstatus": [
        "detached"
    ],
    "entrycreatedate": [
        "20000922072747Z"
    ],
    "username": [
        "rjohnson"
    ],
    "primaryaffiliation": [
        "Staff"
    ],
    "employeeid": [
        "999777666"
    ],
    "sn": [
        "Johnson"
    ]
}
7个回答

286

36
Visual Studio 在“编辑-特殊粘贴”菜单中还有一个“将 JSON 粘贴为类”的选项。 - Jonathan Allen

79

你尝试过使用通用的DeserializeObject方法吗?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

JSON数据中任何缺失的字段应该简单地保留为NULL。

更新:

如果JSON字符串是一个数组,请尝试以下方法:

var jarray = JsonConvert.DeserializeObject<List<MyAccount>>(myjsondata);

jarray 应该是一个 List<MyAccount>

另一个更新:

你收到的异常与对象数组不一致-我认为序列化器在处理你的具有字典类型的 accountstatusmodifiedby 属性时出现了问题。

尝试从序列化中排除 accountstatusmodifiedby 属性,看看是否有所帮助。如果有帮助,你可能需要以不同的方式表示该属性。

文档:使用 Json.NET 进行 JSON 序列化和反序列化


谢谢。但是当它尝试将JSON givenname数组反序列化为GivenName类字符串时,我会收到“无法将JSON数组反序列化为类型'System.String'”的错误。在C#类中定义为字符串的JSON属性始终只是单个元素数组。这就是为什么我开始逐个挑选值,因为在反序列化过程中遇到了这种问题。还有其他我忽略的魔法吗? - user305145
那么...例如 DateTime AccountStatusExpiration,根据代码定义它是不可为空的。要使其可为空,需要做什么?只需将 DateTime 更改为 DateTime? 即可吗? - Hamish Grubijan

8

您可以使用:

JsonConvert.PopulateObject(json, obj);

这里:json是一个JSON字符串,obj是目标对象。详情请见:示例

注意:PopulateObject()不会擦除obj的列表数据,在执行Populate()之后,obj的列表成员将包含其原始数据和来自JSON字符串的数据。


2
它是PopulateObject - Populate不在对象模型中。 - amok
1
这对我来说非常完美!我遇到了一个问题,我有一个相当复杂的JSON字符串,当我尝试将其转换为C#对象时,任何标记为“NotNull”的内容都会从对象中丢失,即使它一开始就存在于JSON字符串中。非常奇怪。我使用了这种方法,它完美地解决了我的问题! - jward01

3
建立在bbant的答案之上,以下是我完整的解决方案,可用于从远程URL反序列化JSON。
using Newtonsoft.Json;
using System.Net.Http;

namespace Base
{
    public class ApiConsumer<T>
    {
        public T data;
        private string url;

        public CalendarApiConsumer(string url)
        {
            this.url = url;
            this.data = getItems();
        }

        private T getItems()
        {
            T result = default(T);
            HttpClient client = new HttpClient();

            // This allows for debugging possible JSON issues
            var settings = new JsonSerializerSettings
            {
                Error = (sender, args) =>
                {
                    if (System.Diagnostics.Debugger.IsAttached)
                    {
                        System.Diagnostics.Debugger.Break();
                    }
                }
            };

            using (HttpResponseMessage response = client.GetAsync(this.url).Result)
            {
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<T>(response.Content.ReadAsStringAsync().Result, settings);
                }
            }
            return result;
        }
    }
}

使用方法如下:

ApiConsumer<FeedResult> feed = new ApiConsumer<FeedResult>("http://example.info/feeds/feeds.aspx?alt=json-in-script");

这里需要翻译的内容是:

其中FeedResult是使用Xamasoft JSON类生成器生成的类。

这是我使用的设置截图,允许奇怪的属性名称,而Web版本无法处理。

Xamasoft JSON Class Generator


2

我发现我错误地构建了我的对象。我使用http://json2csharp.com/ 从JSON生成我的对象类。一旦我有了正确的对象,我就能够顺利地进行类型转换。这是一个初学者的错误。我想提醒你,如果你遇到相同的问题,请仔细检查。


1
您可以尝试在线查看一些类生成器以获取更多信息。不过,我相信其中一些答案是有用的。这里是我的方法,可能会有所帮助。
以下代码是为动态方法而设计的。
dynObj = (JArray) JsonConvert.DeserializeObject(nvm);

foreach(JObject item in dynObj) {
 foreach(JObject trend in item["trends"]) {
  Console.WriteLine("{0}-{1}-{2}", trend["query"], trend["name"], trend["url"]);
 }
}

这段代码基本上允许您访问包含在Json字符串中的成员。这是一种不需要类的不同方式。querytrendurl是Json字符串中包含的对象。

您还可以使用此网站。不要完全信任类,但您可以理解其思想。


-1

假设您的示例数据是正确的,您的givenname和其他条目用括号括起来是JS中的数组... 您将需要使用List来处理这些数据类型,并且使用List来处理accountstatusexpmaxdate... 我认为您的示例中日期格式不正确,所以不确定还有什么其他问题。

这是一篇旧文章,但我想记录下这些问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接