Newtonsoft.json解析匿名对象

本文关键字:对象 json Newtonsoft | 更新日期: 2023-09-27 18:27:41

我很难弄清楚如何用匿名对象解析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类中,然后再进行访问?

Newtonsoft.json解析匿名对象

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

如果停留在静态区域,"数据"字段将是反序列化中最大的问题,因为它在除一个实例之外的所有实例中都是数组类型,而在该实例中它是结构类型。如果没有某种鉴别器,或者字段名和字段类型之间没有一对一的关系,我想不出将这种结构反序列化为强类型图的方法。

假设"数据"始终是一个数组;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()
                                   };            
    }
}

显然,您将需要与现有对象类似的对象,这些对象都源自一个通用的"响应"。

最后一个障碍是知道任何给定元素的具体类型。为此,我建议使用"鉴别器";一种提供简单、唯一类型标识符的通用属性:

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