简体   繁体   中英

How do I deserialize a dynamic json property to object?

I'm trying to deserialize a dynamic JSON (from API) to the correct objects, however some items do not have a type. In the example JSON, the "fulfillment" property has the values "F1" and "F2" and could have more (problem one). Within them, item properties have products order information, but do not have an item type, starting with the product name (ie "03.64.0005_11_10") that can be thousands of options (problem two). How do I deserialise this JSON to fill in the correct objects? I tried RestCharp, Json.net, but I always get stuck on products property that I can not read and fill dynamically.

I tried the answers below, but no success:

How I deserialize a dynamic json property with RestSharp in C#? Deserialize JSON into C# dynamic object?

Can you help me please?

  "billingAddress": {
    "zip": "64001340",
    "state": "PI",
    "number": "3443",
    "status": "ACTIVE",
    "firstName": "Fulano",
    "telephone": {
      "type": "billing",
      "number": "88112244"
    },
    "neighbourhood": "Centro"
  },
  "clientId": "cliente3",
  "documents": [
    {
      "type": "cpf",
      "number": "12345678901"
    }
  ],
  "fulfillments": {
    "F1": {
      "id": "F1",
      "orderId": "4017116",
      "channelId": "channel2",
      "clientId": "cliente3",
      "locationId": "708",
      "shipment": {
        "method": "Economica",
        "carrierName": "Transportadora"
      },
      "status": "CANCELED",
      "type": "SHIPMENT",
      "enablePrePicking": false,
      "items": {
        "03.64.0005_11_10": {
          "sku": "03.64.0005_11_10",
          "quantity": 0,
          "stockType": "PHYSICAL",
          "orderedQuantity": 1,
          "returnedQuantity": 0,
          "canceledQuantity": 1,
          "itemType": "OTHER",
          "presale": false,
          "enablePicking": true
        },
        "18.06.0220_48_2": {
          "sku": "18.06.0220_48_2",
          "quantity": 0,
          "stockType": "PHYSICAL",
          "orderedQuantity": 1,
          "returnedQuantity": 0,
          "canceledQuantity": 1,
          "itemType": "OTHER",
          "presale": false,
          "enablePicking": true
        }
      }
    },
    "F2": {
      "id": "F2",
      "orderId": "4017116",
      "channelId": "channel2",
      "clientId": "cliente3",
      "locationId": "003",
      "operator": {
        "id": "5188",
        "name": "Loja da Vila"
      },
      "ownership": "oms",
      "shipment": {
        "method": "Economica",
        "carrierName": "Transportadora"
      },
      "status": "SHIPPING_READY",
      "type": "SHIPMENT",
      "enablePrePicking": true,
      "items": {
        "18.04.1465_01_3": {
          "sku": "18.04.1465_01_3",
          "quantity": 1,
          "stockType": "PHYSICAL",
          "orderedQuantity": 1,
          "returnedQuantity": 0,
          "canceledQuantity": 0,
          "itemType": "OTHER",
          "presale": false,
          "enablePicking": true
        },
        "18.16.0630_13_10": {
          "sku": "18.16.0630_13_10",
          "quantity": 1,
          "stockType": "PHYSICAL",
          "orderedQuantity": 1,
          "returnedQuantity": 0,
          "canceledQuantity": 0,
          "itemType": "OTHER",
          "presale": false,
          "enablePicking": true
        }
      }
    }
  },
  "createdAt": "2019-06-08T21:41:12.000Z",
  "updatedAt": "2019-06-08T21:41:12.000Z"
}

To

public class BillingAddress
{
    public string zip { get; set; }
    public string state { get; set; }
    public string number { get; set; }
    public string status { get; set; }
    public string firstName { get; set; }
    public Telephone telephone { get; set; }
    public string neighbourhood { get; set; }
}

public class Fulfillment
{
    public string id { get; set; }
    public string orderId { get; set; }
    public string channelId { get; set; }
    public string clientId { get; set; }
    public string locationId { get; set; }
    public Shipment shipment { get; set; }
    public string status { get; set; }
    public string type { get; set; }
    public bool enablePrePicking { get; set; }
    public List<Item> items { get; set; }
}

public class Item
{
    public string sku { get; set; }
    public int quantity { get; set; }
    public string stockType { get; set; }
    public int orderedQuantity { get; set; }
    public int returnedQuantity { get; set; }
    public int canceledQuantity { get; set; }
    public string itemType { get; set; }
    public bool presale { get; set; }
    public bool enablePicking { get; set; }
}

Yea that structure really isn't meant to work like that with JSON. Looks like the fulfillments property should have been an array of those objects instead of having those numbered properties. Kind of looks like this is being auto-generated from an EDI file or something, even with that most of the good conversion tools are smart enough not to do this.

Option A: See if whoever is generating that file for you can correct their process.

Option B: If that is not possible, make your fulfillments property a Dictionary type where fulfillment is the class you have for that inner fulfillment object. That would then deserialize it "properly" and would give you a dictionary you can reference using the "F1" key up to "FN" but ideally you would create a list or array from your dictionary when using it. Even if order mattered you always have the id field to sort by later.

// Property on you deserialization object
public Dictionary<string, Fullfillment> fulfillmentDictionary {get; set;}

// Creating the list for easier use of the data
List<Fullfillment> fulfillments = fulfillmentDictionary.Values.ToList();

Similar logic would apply to your item lists.

If the problem is just that the keys are dynamic but the structure of those dynamically-keyed objects are well-defined, then you can use a Dictionary<string, T> (instead of a List<T> ) to handle the dynamic keys. From your sample JSON it looks like this is the case. So you would need a dictionary for the fulfillments at the root level and the items within the fulfillments . Your classes should look like this:

public class RootObject
{
    public BillingAddress billingAddress { get; set; }
    public string clientId { get; set; }
    public List<Document> documents { get; set; }
    public Dictionary<string, Fulfillment> fulfillments { get; set; }
    public DateTime createdAt { get; set; }
    public DateTime updatedAt { get; set; }
}

public class BillingAddress
{
    public string zip { get; set; }
    public string state { get; set; }
    public string number { get; set; }
    public string status { get; set; }
    public string firstName { get; set; }
    public Telephone telephone { get; set; }
    public string neighbourhood { get; set; }
}

public class Telephone
{
    public string type { get; set; }
    public string number { get; set; }
}

public class Document
{
    public string type { get; set; }
    public string number { get; set; }
}

public class Fulfillment
{
    public string id { get; set; }
    public string orderId { get; set; }
    public string channelId { get; set; }
    public string clientId { get; set; }
    public string locationId { get; set; }
    public Operator @operator { get; set; }
    public string ownership { get; set; }
    public Shipment shipment { get; set; }
    public string status { get; set; }
    public string type { get; set; }
    public bool enablePrePicking { get; set; }
    public Dictionary<string, Item> items { get; set; }
}

public class Operator
{
    public string id { get; set; }
    public string name { get; set; }
}

public class Shipment
{
    public string method { get; set; }
    public string carrierName { get; set; }
}

public class Item
{
    public string sku { get; set; }
    public int quantity { get; set; }
    public string stockType { get; set; }
    public int orderedQuantity { get; set; }
    public int returnedQuantity { get; set; }
    public int canceledQuantity { get; set; }
    public string itemType { get; set; }
    public bool presale { get; set; }
    public bool enablePicking { get; set; }
}

Then deserialize the JSON into the RootObject class:

var root = JsonConvert.DeserializeObject<RootObject>(json);

Here is a working demo: https://dotnetfiddle.net/xReEQh

For JSON messages with dynamic keys (ie changing from message to message) you should start with decoding into dynamic C# objects. One decoded (most JSON parsers support it) you enumerate every dynamic property, then convert its value into a POCO like Fulfillment, Item, etc (or just continue to work with the dynamic object).

Here is an example that works with your JSON https://dotnetfiddle.net/U5NfzC

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM