【问题标题】:Deserialize a complex JSON response without property names反序列化没有属性名称的复杂 JSON 响应
【发布时间】:2021-11-22 17:38:35
【问题描述】:

我想从我的 ASP.Net Core 应用程序中调用 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
    }
  ]
]

这里是 API 网址: 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);

但是有谁知道我会如何处理这种响应类型?

【问题讨论】:

标签: c# json .net-core deserialization httpclient


【解决方案1】:

试试这个。在 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; }
    }

【讨论】:

    【解决方案2】:

    我不知道您是否可以使用 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

    【讨论】:

      【解决方案3】:

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

      我不确定如何删除“标题”,但也许您可以排除“标题”以保留核心数据,然后您会留下一些可能是列表的东西

      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; }
      }
      

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案4】:

      您可以使用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>>();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-15
        • 1970-01-01
        相关资源
        最近更新 更多