Newtonsoft.json解析匿名对象

时间:2014-10-23 18:48:54

标签: c# json json.net

我无法弄清楚如何使用匿名对象解析JSON对象。这是我的JSON:

{
    "success": true,
    "affectedRows": 2,
    "data": [
        {
            "success": true,
            "affectedRows": 1,
            "data": [
                {
                    "ID": 376,
                    "SomeOtherID": 0,
                    "StringValue": "Fan"
                }
            ]
        },
        {
            "success": true,
            "affectedRows": 1,
            "data": []
        },
        {
            "success": true,
            "data": {
                "ID": 401,
                "DateTime": "2014-10-03 18:52:48"
            }
        }
    ]
}

我有一个类,其中包含作为我期待的JSON响应模型的类。

public class JSONObjects
{
    public class Success
    {
        public bool success {get;set}
        public int affectedRows {get;set;}
    }

    public class Response
    {
        public int ID {get;set;}
        public int SomeOtherID {get;set;}
        public string StringValue {get;set;}
    }

    public class FirstResponse : Success
    {
        public Response[] data {get;set;}
    }

    public class SecondResponse : Success
    {
        public object[] data {get;set;}
    }

    public class TimeData
    {
        public int ID {get;set;}
        public string DateTime {get;set;}
    }

    public class FullTimeData
    {
        public bool success {get;set;}
        public TimeData data {get;set;}
    }

    public class FullResponse : Success
    {
        // ??? anonymous object[] data ???
    }
}

用法:

FullResponse data = JsonConvert.DeserializeObject<JSONObjects.FullResponse>(jsonString, jsonSerializerSettings);
Debug.WriteLine(data.data[0].anonymousObject0.data[0].StringValue);

如何将匿名对象作为对象数组放在FullResponse类中,然后再访问它们?

1 个答案:

答案 0 :(得分:1)

嗯,那是个顽固分子。首先,在创建它们的本地上下文之外使用匿名类型的唯一方法是使用dynamic。可以将JSON反序列化为完全动态类型,但NewtonSoft.Json不会开箱即用;可以找到更多信息here。但是,我几乎不惜任何代价避免使用动态,因为它绕过了C#开发人员生成正确代码的最强大的工具;编译器语法检查。 Fat-finger是一个标识符,或者在方法调用中使用方括号而不是括号,只要 可以是有效的C#语法,编译器将不再关心它是否 < / em>有效,让你在运行时找到。

保持静止的土地,&#34;数据&#34;字段将是反序列化中最大的问题,因为它只是一个实例中的一个数组类型,它是一个结构类型。如果没有某种鉴别器,或者字段名称和字段类型之间的一对一关系,我就无法想到一种方法可以将此结构反序列化为强类型图形。

让我们说&#34;数据&#34;永远是一个阵列; JSON中唯一的变化是围绕最后一个对象的实际数据结构的一组方括号:

{
    "success": true,
    "affectedRows": 2,
    "data": [
        {
            "success": true,
            "affectedRows": 1,
            "data": [
                {
                    "ID": 376,
                    "SomeOtherID": 0,
                    "StringValue": "Fan"
                }
            ]
        },
        {
            "success": true,
            "affectedRows": 1,
            "data": []
        },
        {
            "success": true,
            "data": [
                {
                    "ID": 401,
                    "DateTime": "2014-10-03 18:52:48"
                }
            ]
        }
    ]
}

在这种情况下,我要做的是定义一个单一的自嵌套类型,它具有您可以在此图的任何级别找到的每个数据字段的属性:

public class RawResponse
{
    public bool? success {get;set}
    public int? affectedRows {get;set;}

    public int? ID {get;set;}
    public int? SomeOtherID {get;set;}
    public string StringValue {get;set;}
    public string DateTime {get;set;}

    public RawResponse[] data {get;set;}
}

这将允许您将JSON反序列化为静态类型的对象图。然后,您可以根据设置的字段添加一个生成Response的特定派生实现的方法:

public class RawResponse : Response
{
    public bool? success {get;set}
    public int? affectedRows {get;set;}

    public int? ID {get;set;}
    public int? SomeOtherID {get;set;}
    public string StringValue {get;set;}
    public string DateTime {get;set;}

    public RawResponse[] data {get;set;}

    public Response ToResponse()
    {
        if(ID.HasValue && SomeOtherID.HasValue && StringValue.)
            return new OtherIdResponse{
                                           ID = ID, 
                                           SomeOtherID = SomeOtherID, 
                                           StringValue = StringValue
                                      };

        if(ID.HasValue && DateTime.HasValue)
            return new DateTimeResponse{ID = ID, DateTime = DateTime};

        //default case; success and child data with optional affectedRows
        return new CompoundResponse{
                                       success = success, 
                                       affectedRows = affectedRows, // can be null 
                                       data = data.Select(d=>d.ToResponse())
                                                 .ToArray()
                                   };            
    }
}

显然,您需要与您已有的对象类似的对象,这些对象都来自一个共同的回应&#34;。

最后一个障碍是知道任何给定元素的具体类型。为此,我推荐一个&#34;鉴别器&#34 ;;提供简单,唯一类型标识符的公共属性:

public abstract class Response
{
    public string ReponseTypeName{ get { return GetType().Name; } }
}