对不带属性名的复杂JSON响应进行反序列化



我想从我的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; }
}

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

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

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

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

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

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

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尽管生成了大量的代码,但生成解决方案看起来会很成功。如果有一种简单的方法可以移除页眉,下面的方法将是更好的选择。

我不知道如何删除";标题";,但也许你可以排除";标题";留下核心数据,然后你会留下一些可能是List的东西

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

您可以使用System.Text.Json中的JsonDocument将顶级数组中的第一个和第二个项反序列化为单独的对象,而无需使用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>>();

最新更新