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


144

我是刚接触C#和JSON数据的新手,正在寻求指导。我正在使用C#3.0,.NET3.5SP1和JSON.NET 3.5r6。

我有一个需要从JSON结构填充的已定义C#类。但是,并非从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"
    ]
}

Answers:



76

您是否尝试过使用通用的DeserializeObject方法?

JsonConvert.DeserializeObject<MyAccount>(myjsondata);

JSON数据中任何缺少的字段都应简单地留为NULL。

更新:

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

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

jarray然后应该是一个List<MyAccount>

另一个更新:

您得到的异常与对象数组不一致-我认为序列化程序在使用Dictionary-typed accountstatusmodifiedby属性时遇到问题。

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

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


谢谢。但是,我收到“无法将JSON数组反序列化为'System.String'类型的错误”的错误。尝试将JSON Givenname数组反序列化(例如)到类GivenName字符串中时。我在C#类中定义为字符串的JSON属性仅是单个元素数组。这就是为什么在反序列化过程中遇到此类问题时,我开始一个接一个地选择值的原因。我忽略的其他魔术?

所以... DateTime AccountStatusExpiration(例如)不是代码中定义的可为空。要使其变为可空值会怎样?只需更改DateTimeDateTime?
Hamish Grubijan

50

答案转载自https://stackoverflow.com/a/10718128/776476

您可以使用C#dynamic类型简化事情。由于不依赖魔术字符串,该技术还使重构更加简单。

杰森

json下面的字符串是来自http api调用的简单响应,它定义了两个属性:IdName

{"Id": 1, "Name": "biofractal"}

C#

用于JsonConvert.DeserializeObject<dynamic>()将该字符串反序列化为动态类型,然后以通常的方式简单地访问其属性。

var results = JsonConvert.DeserializeObject<dynamic>(json);
var id = results.Id;
var name= results.Name;

注意:NewtonSoft程序集的NuGet链接是http://nuget.org/packages/newtonsoft.json。不要忘记添加:using Newtonsoft.Json;访问那些类。


7
喜欢使用<dynamic>。但是,我必须这样做才能使其正常工作:result [“ Id”]。Value和result [“ Name”]。Value
fredw 2014年

1
虽然动态是一个不错的选择,但以上示例并未使用“魔术字符串”,而是使用了强类型的泛型,这些泛型在常规VS重构技术期间进行了更新(除非在MVC中的View内,不在本问题的范围之内)。
gcoleman0828

4
您仍然拥有“魔术字符串”,它们现在仅通过使用动态隐藏!
伊恩·

的确,正如@IanRingrose指出的那样,生物分形技术使它倒退为“魔术弦”。根据DeserializeObject<dynamic>定义,返回的动态对象未经过“类型检查”。所以下面的线results.Id,并results.Name不能在编译时进行验证。任何错误都将在运行时发生。将此与强类型调用(例如)进行对比DeserializeObject<List<MyAccount>>可以在编译时确认对这些属性的引用。dynamic尽管可能方便,但使用会引入“魔术字符串”作为属性名称;降低鲁棒性恕我直言。
制造商史蒂夫(Steve),

8

您可以使用:

JsonConvert.PopulateObject(json, obj);

此处:json是json字符串,obj是目标对象。请参阅:示例

注意:PopulateObject()不会删除obj的列表数据,在之后Populate()obj's列表成员将包含其原始数据和json字符串中的数据


2
它是PopulateObject-填充不在对象模型中。
amok 2012年

1
这对我来说是完美的!我遇到的问题是我有一个非常复杂的JSON字符串,当我尝试将其强制转换为C#对象时,该对象中会丢失任何标有'NotNull'的内容,即使它最初存在于JSON字符串中也是如此。很奇怪。我使用了这种方法,并且效果很好!
jward01 '16

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类生成器生成的类在哪里

这是我使用的设置的屏幕截图,允许使用网络版本无法解释的奇怪属性名称。

Xamasoft JSON类生成器


1

我发现自己没有正确构建对象。我使用http://json2csharp.com/从JSON生成了我的对象类。一旦有了正确的Oject,我就可以毫无问题地进行投射。Norbit,Noob错误。以为如果您遇到相同的问题,我会添加它。


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字符串的对象。

您也可以使用该网站。不要相信班级100%,但您会明白。


0

假设样本数据正确,给定名称以及括在方括号中的其他条目都是JS中的数组,那么您将希望使用List作为这些数据类型。和清单accountstatusexpmaxdate ...的清单...我想您的示例中的日期格式有误,但是不确定示例中的其他错误。

这是旧文章,但想记下这些问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.