反序列化没有 属性 名称的复杂 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; }
}
我想从我的 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; }
}