C#中用于反序列化可以是数组或单个对象的json对象的最佳方法

时间:2014-11-29 16:55:53

标签: c# json

我们正在处理json API结果。只是为了让我们的生活变得困难,API的提供者如果有多个对象则将​​项目作为数组返回,或者如果只有一个对象则作为单个对象返回。

e.g。

如果只有一个对象......

{
  propertyA: {
    first: "A",
    second: "B"
  }
}

或者如果有多个:

{
  propertyA: [
    {
      first: "A",
      second: "B"
    },
    {
      first: "A",
      second: "B"
    }
  ]
}

有没有人有办法处理这种情况? 理想情况下,我们希望将两者序列化为

public class ApiResult{
  public ApiItem[] PropertyA {get;set;}
}

这适用于第二个示例,但是在遇到第一个示例时,您会遇到类型为' System.MissingMethodException'的第一次机会异常。发生在System.Web.Extensions.dll

附加信息:没有为' ApiItem []'的类型定义无参数构造函数。

4 个答案:

答案 0 :(得分:2)

我假设类定义如下

public class ApiResult
{
    public ApiItem[] PropertyA { get; set; }
}

public class ApiItem
{
    public string First { get; set; }
    public string Second { get; set; }
}

您可以将json反序列化为动态变量,然后检查d.propertyA的类型。如果它是JArray,那么propertyA是一个数组,因此您可以将json反序列化为ApiResult。如果它是JObject,那么propertyA是一个对象,因此您需要手动构建ApiItem并将其分配给PropertyA ApiResult。考虑下面的方法

public ApiResult Deserialize(string json)
{
    ApiResult result = new ApiResult();
    dynamic d = JsonConvert.DeserializeObject(json);
    if (d.propertyA.GetType() == typeof (Newtonsoft.Json.Linq.JObject))
    {
        // propertyA is a single object, construct ApiItem manually
        ApiItem item = new ApiItem();
        item.First = d.propertyA.first;
        item.Second = d.propertyA.second;

        // assign item to result.PropertyA[0]
        result.PropertyA = new ApiItem[1];
        result.PropertyA[0] = item;
    }
    else if (d.propertyA.GetType() == typeof (Newtonsoft.Json.Linq.JArray))
    {
        // propertyA is an array, deserialize json into ApiResult
        result = JsonConvert.DeserializeObject<ApiResult>(json);
    }

    return result;
}

上面的代码将为两个json示例返回ApiResult的实例。

工作演示:https://dotnetfiddle.net/wBQKrp

答案 1 :(得分:0)

向/从JSON序列化/反序列化的最佳方法是Json.NET

  

.NET的流行高性能JSON框架

Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Sizes = new string[] { "Small" };

string json = JsonConvert.SerializeObject(product);
//{
//  "Name": "Apple",
//  "Expiry": "2008-12-28T00:00:00",
//  "Sizes": [
//    "Small"
//  ]
//}

string json = @"{
  'Name': 'Bad Boys',
  'ReleaseDate': '1995-4-7T00:00:00',
  'Genres': [
    'Action',
    'Comedy'
  ]
}";

Movie m = JsonConvert.DeserializeObject<Movie>(json);

string name = m.Name;
// Bad Boys

答案 2 :(得分:0)

JSON#有一个非常轻量级的工具,可以让你实现这一目标。无论嵌入式JSON是否是更大的JSON对象中的对象或数组,它都将检索嵌入式JSON:

const string schoolMetadata = @"{ "school": {...";
var jsonParser = new JsonObjectParser();

using (var stream = 
    new MemoryStream(Encoding.UTF8.GetBytes(schoolMetadata))) {
    Json.Parse(_jsonParser, stream, "teachers");
}

在这里,我们从较大的“学校”对象中检索“教师”对象。

答案 3 :(得分:0)

ekad's answer的基础上,我使代码更易于维护(因为现在您没有在ApiItem中指定每个属性),并且更易于引入其他属性(如果已经存在并且数组):

static ApiResult Deserialize(string json)
{
    JObject j = JObject.Parse(json);
    var propA = j["propertyA"];
    switch (propA.Type.ToString())
    {
        case "Object":
            return new ApiResult {
                PropertyA = new[]{propA.ToObject<ApiItem>()},
                SomethingElse = j["somethingElse"].ToObject<string>(),
            };
        case "Array":
            return j.ToObject<ApiResult>();
        default:
            throw new Exception("Invalid json with propertyA of type " + propA.Type.ToString());
    }
}

对于这些示例类(我刚刚添加了SomethingElse):

public class ApiResult
{
    public ApiItem[] PropertyA { get; set; }
    public string SomethingElse { get; set; }
}
public class ApiItem
{
    public string First { get; set; }
    public string Second { get; set; }
}

正在运行的演示:https://dotnetfiddle.net/VLbTMu