Difficulty parsing JSON response in Unity
-
I'm purchasing some item using the following code (example from BrainCloud API reference):
_bc.UserItemsService.PurchaseUserItem(defId, quantity, shopId, includeDef, successCallback, failureCallback);
I get the following JSON in response (example from BrainCloud API reference):
{ "data": { "items": { "8e12e907-3f54-4971-b84a-b685ce50b684": { "itemId": "8e12e907-3f54-4971-b84a-b685ce50b684", "defId": "boost_rapidfire", "quantity": 1, "usesLeft": null, "coolDownStart": -1, "recoveryStart": -1, "itemData": {}, "giftedTo": null, "giftedFrom": null, "blockId": null, "createdAt": 1566850132554, "updatedAt": 1566850132554, "version": 1, "maxUses": null, "coolDownUntil": -1, "recoveryUntil": -1, "itemDef": { "defId": "boost_rapidfire", "name": "Rapid Fire", "desc": "Rapid fire for the next match.", "type": "ITEM", "category": "boost", "tags": null, "buyPrice": { "coins": 200 }, "sellPrice": {}, "image": null, "resourceGroup": null, "resourceTag": null, "meta": {}, "pState": "PUBLISHED", "publishedAt": 1566850042148, "stackable": false, "consumable": false, "uses": null, "coolDownSecs": 0, "recoverySecs": 0, "activatable": true, "statusName": "boosted", "activeSecs": 900000, "tradable": false, "blockchain": false, "blockchainDefId": null } } }, "currencySpent": { "coins": 200 }, "currencyBalances": { "createdAt": 1566850026783, "currencyMap": { "coins": { "consumed": 200, "balance": 2999800, "purchased": 0, "awarded": 3000000 } }, "playerId": "55ffc16e-f92b-44f3-98b0-68a7a4f24106", "updatedAt": 1566850132552 } }, "status": 200 }
Basically we have the following hierarchy of JSON keys:
data.items["8e12e907-3f54-4971-b84a-b685ce50b684"].itemId
and so on.
The problem with this response is it uses UUID8e12e907-3f54-4971-b84a-b685ce50b684
as a JSON key which is obviously not known in advance. Hence, I cannot use built-in Unity's JsonUtility library to map this JSON response to a C# object, like this:[System.Serializable] private class PurchaseUserItemSuccessResponse { public Data data; [System.Serializable] public class Data { public Items items; [System.Serializable] public class Items { // The problem: // Can't define specific items here // because their keys are not known in advance } } }
Of course, I can try to use some preprocessing tricks or to utilize external JSON library to overcome the issue, but still, using dynamic keys in JSON response for me looks like a bad practice because it creates unnecessary obstacles. It would be simpler to expect solid JSON keys structure. As a developer, for this specific case, I would have liked to see results as a simple array, like this:
{ "data": { "items": [ "itemId": "8e12e907-3f54-4971-b84a-b685ce50b684", "defId": "boost_rapidfire", "quantity": 1, "usesLeft": null, "coolDownStart": -1, "recoveryStart": -1, "itemData": {}, "giftedTo": null, "giftedFrom": null, "blockId": null, "createdAt": 1566850132554, "updatedAt": 1566850132554, "version": 1, "maxUses": null, "coolDownUntil": -1, "recoveryUntil": -1, "itemDef": { "defId": "boost_rapidfire", "name": "Rapid Fire", "desc": "Rapid fire for the next match.", "type": "ITEM", "category": "boost", "tags": null, "buyPrice": { "coins": 200 }, "sellPrice": {}, "image": null, "resourceGroup": null, "resourceTag": null, "meta": {}, "pState": "PUBLISHED", "publishedAt": 1566850042148, "stackable": false, "consumable": false, "uses": null, "coolDownSecs": 0, "recoverySecs": 0, "activatable": true, "statusName": "boosted", "activeSecs": 900000, "tradable": false, "blockchain": false, "blockchainDefId": null ] }, "currencySpent": { "coins": 200 }, "currencyBalances": { "createdAt": 1566850026783, "currencyMap": { "coins": { "consumed": 200, "balance": 2999800, "purchased": 0, "awarded": 3000000 } }, "playerId": "55ffc16e-f92b-44f3-98b0-68a7a4f24106", "updatedAt": 1566850132552 } }, "status": 200 }
Mapping this response to a C# object is trivial.
-
Hello! Thanks for your inquiry.
The format of this response has been used for several years so to change it at this point would cause many errors for our clients. However, in situations like this, we have included the JsonFx library to help deserialize dynamic JSON objects with.
One way to deserialize the JSON items would be to include a
DeserializeItems(string)
method in yourData
class to be able to store them in an array using JsonFx'sJsonReader.Deserialize(string)
method:public List<Item> Items = new List<object>(); public void DeserializeItems(string jsonResponse) { var response = JsonReader.Deserialize<Dictionary<string, object>>(jsonResponse); var data = response["data"] as Dictionary<string, object>; var items = data["items"] as Dictionary<string, object>; foreach (Dictionary<string, object> item in items.Values) { var newItem = new Item(); newItem.itemId = (string)item["itemId"]; newItem.defId = (string)item["boost_rapidfire"]; newItem.quantity = (int)item["quantity"]; // etc... Items.Add(newItem); } }
Alternatively, you can also use JsonFx's
JsonWriter.Serialize(object)
method in the foreach loop to then be able to use Unity's JsonUtility to automatically map it as an Item.Understandably, both methods have their own pros and cons. However, it should be able to get the job done in this case.
Hope this helps! Please let us know if you have further questions or inquiries about this.