2017-07-17 51 views
0

我正在与各种端点上的API进行通信。所有响应在meta中都有一些共享字段,然后是一个可包含任意数量自定义对象的data对象。CustomJsonConverter将一个字段反序列化为JSON,其中一个字段可能是多个自定义对象

的几个例子: 1)一种有效载荷返回data含有accounts[]

{ 
"meta": { 
    "timestamp": "2017-07-14T12:59:34-04:00", 
    "code": 200 
}, 
"data": { 
    "accounts": [ 
     { 
      "address": "Main Street", 
      "country": "US", 
      "email": "[email protected]", 
      "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是可以访问的。

这样做的破解方式似乎创建了一个具有所有可能的对象类型的类,所以当序列化/反序列化发生时,那些不存在的就是空。

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做到这一点。萨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

*我想知道是否有一种方法可以使用自定义的JsonConverter * - 你想让你的'Data'类型看起来像什么?你在哪里应用转换器? – dbc

+0

对“数据”的外观没有什么看法 - 只会更喜欢不必'{get; set;}'为每个可能的对象类型(只给出了两个例子,但我正在处理6+对象类型''数据'可以包含)。添加了我在上面应用转换器的示例 – dbJones

+0

当您碰到相应的端点时,API是否具有有关它在数据中返回的对象类型的文档?它必须公开这些信息。 – niksofteng

回答

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>"); 
+0

通用api请求方法的奖励分数。在我的思想中没有得到那么多。谢谢! – dbJones

0

如果你把你的数据类只是对象

public class Data 
{ 
    public object[] ResponsePayload {get;set;} 
} 
+0

使用json.net进行反序列化时,这会导致问题,因为上面的属性名称('ResponsePayload')与json中的任何字段名称不匹配。 Usin'[JsonProperty(“shipments”)]'作为映射工作之一,但是每个字段只能放置一个'JsonProperty'。我正在处理6个以上的对象,每个对象都有自己的名字,所以这种方法在这种情况下不起作用 – dbJones

+0

你的JSON响应在其中不会有“Shipment”或“Account”字段,但所有这些对象都可以存储在一个对象数组中。但是你说你需要使用指向数据的json响应关键字,这取决于数据类型吗? – victor

0

的阵列可以直接从您的HttpClient做到这一点:

var myResponsePayload = await response.Content.ReadAsAsync<MyTypeHere>().ConfigureAwait(false); 

当你打你的API,他们”我会以这种格式进来。只要确保你想在你的类中设置的属性与JSON中的内容对齐即可。

如果您没有访问Web服务,而是从文件或数据库中读取数据,我建议使用Newtonsoft Json,这是一种广泛使用的用于json反序列化的NuGet包。

你可能最终得到这样的:

var results = _serializer.Deserialize<IEnumerable<MyBingyMathob>>(reader); 

这将工作,只要你序列与公众制定者有公开访问的属性的类。如果你不想要所有的数据,那么丢失的字段将被丢弃。

+0

上面概述的自定义转换器是Json.Net(又名Newtonsoft Json)的内置部分。所以试图走这条路。 – dbJones

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

希望它能帮助。

相关问题