反序列化没有 属性 名称的复杂 JSON 响应

Deserialize a complex JSON response without property names

我想从我的 ASP.Net 核心应用程序调用 API,但响应格式不佳:

[
  {
    page: 1,
    pages: 1,
    per_page: 50,
    total: 2,
    sourceid: "2",
    sourcename: "World Development Indicators",
    lastupdated: "2021-10-28"
  },
  [
    {
      indicator: {
        id: "NY.GDP.PCAP.KD.ZG",
        value: "GDP per capita growth (annual %)"
      },
      country: {
        id: "CA",
        value: "Canada"
      },
      countryiso3code: "CAN",
      date: "2020",
      value: -6.33904652535297,
      unit: "",
      obs_status: "",
      decimal: 1
    },
    {
      indicator: {
        id: "NY.GDP.PCAP.KD.ZG",
        value: "GDP per capita growth (annual %)"
      },
      country: {
        id: "CA",
        value: "Canada"
      },
      countryiso3code: "CAN",
      date: "2019",
      value: 0.43021113414872,
      unit: "",
      obs_status: "",
      decimal: 1
    }
  ]
]

这里是APIurl: https://api.worldbank.org/v2/country/can/indicator/NY.GDP.PCAP.KD.ZG?format=json&mrv=2

我输入了这些 类 作为回复:

public class WorldBankApiResponseGlobal
  {
    public int Page { get; set; }
    public int Pages { get; set; }
    public int Per_Page { get; set; }
    public int Total { get; set; }
    public string Sourceid { get; set; }
    public string Sourcename { get; set; }
  }

  public class WorldBankApiIdValue
  {
    public string Id { get; set; }
    public string Value { get; set; }
  }

  public class WorldBankApiResponse
  {
    public WorldBankApiIdValue Indicator { get; set; }
    public WorldBankApiIdValue Country { get; set; }
    public int Value { get; set; }
    public string Date { get; set; }
    public string Countryiso3code { get; set; }
  }

但是,由于响应没有对象的 属性 值(即:[{},[]] 而不是 ["data": {}, "indicators": []] ,我不知道如何正确反序列化 API 调用响应...

通常情况下,我会这样做:

var response = await _client.GetFromJsonAsync<List<WorldBankApiResponseGlobal>>(worldBankApiUrl);

但是有人知道我将如何处理这种响应类型吗?

我不知道你是否可以使用 GetFromJsonAsync 但使用 Newtonsoft.Json。您可以设计一个特殊的转换程序来检查令牌是数组还是对象。并反序列化为适当的类型。

class WorldBankApiResponseUnionConverter : JsonConverter
{
    public override bool CanConvert(Type t) => t == typeof(WorldBankApiResponseUnion) || t == typeof(WorldBankApiResponseUnion?);

    public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
    {
        switch (reader.TokenType)
        {
            case JsonToken.StartObject:
                var objectValue = serializer.Deserialize<WorldBankApiResponseGlobal>(reader);
                return new WorldBankApiResponseUnion { WorldBankApiResponseGlobal = objectValue };
            case JsonToken.StartArray:
                var arrayValue = serializer.Deserialize<WorldBankApiResponse[]>(reader);
                return new WorldBankApiResponseUnion { WorldBankApiResponse = arrayValue };
        }
        throw new Exception("Cannot unmarshal type WorldBankApiResponseUnion");
    }

现场演示:https://dotnetfiddle.net/3Rqnya
使用自动化工具:https://app.quicktype.io/?l=csharp

编辑:上面发布 https://app.quicktype.io/?l=csharp 以生成解决方案的评论看起来可以解决问题,尽管它会生成大量代码。如果有一种简单的方法可以删除 header,下面的方法会是更好的选择。

我不确定如何删除“header”,但也许你可以排除“header”以保留核心数据,然后你会留下一些可以列表

public class WorldBankApiResponse
{
  public WorldBankApiIdValue Indicator { get; set; }
  public WorldBankApiIdValue Country { get; set; }
  public int Value { get; set; }
  public string Date { get; set; }
  public string Countryiso3code { get; set; }
}

您可以使用 JsonDocument from System.Text.Json 将顶级数组中的第一项和第二项反序列化为单独的对象,而无需使用 GetFromJsonAsync。

var json = @"
[
  { ""page_count"": 10 },
  [
    { ""id"": 1 },
    { ""id"": 2 }
  ]
]
";

class Header
{
    public int page_count { get; set; }
}
class Item
{
    public int id { get; set; }
}

using var document = JsonDocument.Parse(json);
var header = document.RootElement[0].Deserialize<Header>();
var body = document.RootElement[1].Deserialize<List<Item>>();

如果您使用的是 Newtonsoft.Json,则等效项是 JArray(通常是 JToken):

var jarray = JArray.Parse(json);
var header = jarray[0].ToObject<Header>();
var body = jarray[1].ToObject<List<Item>>();

试试这个。它在 Visual Studio

中进行了测试
var parsedArray=JArray.Parse(json);

// this is just an array part
List<WorldBankApiResponse> worldBankApiResponses =  
JsonConvert.DeserializeObject<List<WorldBankApiResponse>>(parsedArray[1].ToString());

// this is just a header part
var wb = 
JsonConvert.DeserializeObject<WorldBankApiResponseGlobal>(parsedArray[0].ToString());

//this is the whole object
 var worldBankApiResponseGlobal= new WorldBankApiResponseGlobal{
     Page=wb.Page,
     Pages=wb.Pages,
     PerPage=wb.PerPage,
     Total=wb.Total,
     Sourceid=wb.Sourceid,
     Sourcename=wb.Sourcename,
     Lastupdated=wb.Lastupdated,
     WorldBankApiResponses=  worldBankApiResponses
 };

public partial class WorldBankApiResponseGlobal
    {
        [JsonProperty("page")]
        public long Page { get; set; }

        [JsonProperty("pages")]
        public long Pages { get; set; }

        [JsonProperty("per_page")]
        public long PerPage { get; set; }

        [JsonProperty("total")]
        public long Total { get; set; }

        [JsonProperty("sourceid")]
        public long Sourceid { get; set; }

        [JsonProperty("sourcename")]
        public string Sourcename { get; set; }

        [JsonProperty("lastupdated")]
        public DateTimeOffset Lastupdated { get; set; }

        [JsonProperty("WorldBankApiResponses")]
        public List<WorldBankApiResponse> WorldBankApiResponses { get; set; }
    }

    public partial class WorldBankApiResponse
    {
        [JsonProperty("indicator")]
        public WorldBankApiIdValue Indicator { get; set; }

        [JsonProperty("country")]
        public WorldBankApiIdValue Country { get; set; }

        [JsonProperty("countryiso3code")]
        public string Countryiso3Code { get; set; }

        [JsonProperty("date")]
        public long Date { get; set; }

        [JsonProperty("value")]
        public double Value { get; set; }

        [JsonProperty("unit")]
        public string Unit { get; set; }

        [JsonProperty("obs_status")]
        public string ObsStatus { get; set; }

        [JsonProperty("decimal")]
        public long Decimal { get; set; }
    }

    public partial class WorldBankApiIdValue
    {
        [JsonProperty("id")]
        public string Id { get; set; }

        [JsonProperty("value")]
        public string Value { get; set; }
    }