我正在与各种端点上的API进行通信。所有回复都在meta
中有一些共享字段,然后是一个data
对象,可以包含任意数量的自定义对象。
一些例子:
1)返回data
的有效负载包含accounts[]
{
"meta": {
"timestamp": "2017-07-14T12:59:34-04:00",
"code": 200
},
"data": {
"accounts": [
{
"address": "Main Street",
"country": "US",
"email": "1@23.com",
"city": "MyTown",
"accountName": "MyAccount",
"account": 123456,
"contact": ""
}
]
}
}
2)有效负载返回data
包含shipments[]
{
"meta": {
"timestamp": "2017-07-14T13:46:42-04:00",
"code": 400
},
"data": {
"shipments": [
{
"distributionCenter": "123",
"packages": [
{
"packageDetails": {},
"consigneeAddress": {},
"errors": [
{
"errorCode": "VALIDATION_ERROR",
"errorId": 0,
"errorMessage": "INVALID_LOGIN"
}
],
],
"status": {
"timestamp": "2017-07-14T13:46:42-0400",
"numAccepted": 0,
"numRejected": 1,
"code": "ERROR"
},
}
]
}
}
当我点击shipments[]
端点时,我需要有shipments
的访问权限,当遇到accounts[]
端点时,我需要访问accounts
。这样data.accounts[0].email
可以访问。
执行此操作的hack方式似乎是创建一个具有所有可能对象类型的Data
类,因此在序列化/反序列化发生时,那些不存在的类只是null。
public class ApiResponse
{
public Meta meta { get; set; }
public Data data { get; set; }
}
public class Meta
{
public string code { get; set; }
public DateTime timestamp { get; set; }
}
public class Data
{
public Account[] accounts { get; set; }
public Shipment[] shipments { get; set; }
}
但我想知道是否有办法用Newtonsoft JSON自定义JsonConverter来做到这一点。 Sa Data
类看起来像:
[JsonConverter(typeof(DataConverter))]
public class Data
{
\\not sure what to put here...
}
有关如何让ReadJson识别自定义对象的任何输入?或者在其他json.net上实现这个目标的方式?
class DataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Data));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value.GetType() == Shipment)
/// this throws error "not valid type in this context"
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
答案 0 :(得分:3)
我会重新编写类以提供不同的data
类型。首先,我只需要一个通用ApiResponse<T>
,如下所示:
public class ApiResponse<T> where T : class {
public Meta Meta { get; set; }
public T Data { get; set; }
}
public class AccountData {
public Account[] Accounts { get; set; }
}
public class ShipmentData {
public Shipment[] Shipments { get; set; }
}
不要对数据的外观有什么看法
当您遇到特定的终点时,您就会知道将返回什么API。如果您要求账户,它不会给您发货或其他东西,反之亦然。如果可用,请查看API文档。
此外,您可以简单地创建一个通用API请求方法,以询问不同类型的数据,即AccountData,ShipmentData等。
ApiResponse<T> MakeApiRequest<T>(string apiUrl) where T : class {
// Call API the way you want and get JSON response
var jsonResponse = "{ \"data\": { \"accounts\": [ { \"address\": \"Main Street\" } ] } }"; // Example data hard-coded
// Desrialize JSON response into ApiResponse<T>
var apiResponse = JsonConvert.DeserializeObject(jsonResponse, typeof(ApiResponse<T>)) as ApiResponse<T>;
return apiResponse;
}
使用此方法调用端点:
var accountApiResponse = MakeApiRequest<AccountData>("<accounts_api_url>");
var shipmentApiResponse = MakeApiRequest<ShipmentData>("<shipments_api_url>");
答案 1 :(得分:1)
您需要将任何特定数据移动到这样的子类:
public class AccountData
{
public Account[] accounts { get; set; }
}
public class ShipmentData
{
public Shipment[] shipments { get; set; }
}
从那里你可以从使用一个响应类到一个具有公共数据的根的抽象,以及具有如下特定信息的两个具体类:
public abstract class ApiResponse
{
public Meta meta { get; set; }
}
public class AccountApiResponse : ApiResponse
{
public AccountData data { get; set; }
}
public class ShipmentApiResponse : ApiResponse
{
public ShipmentData data { get; set; }
}
当您拨打帐户终端时,可以通过以下方式检索您的数据:
var accountResponse = JsonConvert.DeserializeObject<AccountApiResponse>(json);
对于货件:
var shipmentResponse = JsonConvert.DeserializeObject<ShipmentApiResponse>(json);
希望它有所帮助。
答案 2 :(得分:0)
如果使Data类只是一个对象数组
,该怎么办?public class Data
{
public object[] ResponsePayload {get;set;}
}
答案 3 :(得分:0)
您可以直接从HttpClient
:
var myResponsePayload = await response.Content.ReadAsAsync<MyTypeHere>().ConfigureAwait(false);
当您点击API时,他们会以这种格式进入。只需确保您要在班级中设置的属性与JSON中的内容对齐。
如果您没有访问网络服务,而是从文件或数据库中读取数据,我建议Newtonsoft Json,这是一个广泛使用的用于json反序列化的NuGet包。
你最终可能会遇到这样的事情:
var results = _serializer.Deserialize<IEnumerable<MyBingyMathob>>(reader);
只要您序列化的类具有公共setter的公共可访问属性,这将有效。如果您不想要所有数据,则会删除丢失的字段。