How do I use LitJson to edit single attributes of multiple instances of one class all stored in one json file?

1.2k Views Asked by At

I am trying to create an inventory system using a json file. Basically, I have my json file:

[
    {
    "name":"Potion",
    "id":1,
    "balance":5
    },
    {
    "name":"Neutral Bomb",
    "id":2,
    "balance": 4
    }
]

And I have my c# file:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using LitJson;


public class Item
{
    public string Name  { get; set; }
    public int Id       { get; set; }
    public int Balance  { get; set; }

    public Item(string name1, int id1, int balance1) {
        Name = name1;
        Id = id1;
        Balance = balance1;
    }
}

public class InventoryAttempt : MonoBehaviour
{
    public static void BalanceChange(string filePath, int ID, int change)
    {
        string jsonString = File.ReadAllText(Application.dataPath + filePath);
        List<Item> itemList = JsonMapper.ToObject<List<Item>>(jsonString);
        itemList [ID].Balance += change;
        jsonString = JsonMapper.ToJson (itemList).ToString();
        File.WriteAllText (Application.dataPath + filePath, jsonString);
    }

    void Start()
    {
        BalanceChange ("/Scripts/inventory.json", 1, 1);
    }
}

In my c# file I want to access a single item, lets say the neutral bomb item. How can I go in and edit the balance of the neutral bomb? I want to use the Item ID to tell which object to edit. I want to create a list where each element is one of the items that are stored in the Json file, but I'm not sure how to separate them. What should I do?

MissingMethodException: Method not found: 'Default constructor not found...ctor() of Item'.
System.Activator.CreateInstance (System.Type type, Boolean nonPublic) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Activator.cs:368)
System.Activator.CreateInstance (System.Type type) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Activator.cs:254)
LitJson.JsonMapper.ReadValue (System.Type inst_type, LitJson.JsonReader reader)
LitJson.JsonMapper.ReadValue (System.Type inst_type, LitJson.JsonReader reader)
LitJson.JsonMapper.ToObject[List`1] (System.String json)
InventoryAttempt.BalanceChange (System.String filePath, Int32 ID, Int32 change) (at Assets/Scripts/InventoryAttempt.cs:26)
InventoryAttempt.Start () (at Assets/Scripts/InventoryAttempt.cs:34)
2

There are 2 best solutions below

0
On BEST ANSWER

First of all, the variable names in your Json does not match the ones in your Item class. The ones in your Item class are capitalized.

Use Unity's JsonUtility.

Secondly, get the JsonHelper class from this post. It allows Unity's JsonUtility to serialize and deserialize Json Array.

Do not generate Json by hand in the future since you will run into problems like this. Use the Json class and a simple function to do that. For example, this:

void generateJson()
{
    Item[] item = new Item[2];

    item[0] = new Item();
    item[0].Name = "Potion";
    item[0].Id = 1;
    item[0].Balance = 5;

    item[1] = new Item();
    item[1].Name = "Neutral Bomb";
    item[1].Id = 2;
    item[1].Balance = 4;

    string value = JsonHelper.ToJson(item, true);
    Debug.Log(value);
}

Will generate this:

{
    "Items": [
        {
            "Name": "Potion",
            "Id": 1,
            "Balance": 5
        },
        {
            "Name": "Neutral Bomb",
            "Id": 2,
            "Balance": 4
        }
    ]
}

No error. That is valid json.

Solution:

InventoryAttempt class:

public class InventoryAttempt
{
    public static void BalanceChange(string filePath, int ID, int change)
    {
        //Load Data
        string jsonString = File.ReadAllText(Application.dataPath + filePath);

        Item[] item = JsonHelper.FromJson<Item>(jsonString);

        //Loop through the Json Data Array
        for (int i = 0; i < item.Length; i++)
        {
            //Check if Id matches
            if (item[i].Id == ID)
            {
                Debug.Log("Found!");
                //Increment Change value?
                item[i].Balance += change;
                break; //Exit loop
            }
        }

        //Convert to Json
        string newJsonString = JsonHelper.ToJson(item);

        //Save
        File.WriteAllText(Application.dataPath + filePath, newJsonString);
    }
}

Your Item class Item.

[Serializable]
public class Item
{
    public string Name;
    public int Id;
    public int Balance;

    public Item()
    {

    }

    public Item(string name1, int id1, int balance1)
    {
        Name = name1;
        Id = id1;
        Balance = balance1;
    }
}

Test script:

public class Test : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
        InventoryAttempt.BalanceChange("/Scripts/inventory.json", 1, 1);
    }
}

Note:

I removed get/set property stuff. Please remove these as they must be removed for this to work. Also, [Serializable] is added to the top of the Item class. Please add that as-well. I suggest you copy and replace the classes directly before making the "it doesn't work" comment under the question...

13
On

First things first your json does not seem to be a valid array (not the missing [ and ].

[
    {
        "name":"Potion"
        "id":1,
        "balance":7
    },
    {
        "name":"Neutral Bomb",
        "id":2,
        "balance": 4
    }
]

Also, I would follow naming conventions and start the properties with an Upper Case characters.

public class Item
{
    public string Name  { get; set; }
    public int Id       { get; set; }
    public int Balance  { get; set; }

    public Item()
    {
    }

    public Item(string name1, int id1, int balance1)
    {
        Name = name1;
        Id = id1;
        Balance = balance1;
    }
}

The code below should give you want you want, but depending on the scope of your project could be very inefficient. You may want to consider caching the jsonString and/or the deserialization result. You may also want to use FirstOrDefault and use a null check if its expected the ID may not exist.

public class InventoryAttempt : MonoBehaviour
{
    public static Item JsonToItem(string filePath, int itemID)
    {
        string jsonString = File.ReadAllText(Application.dataPath + filePath);
        return JsonMapper.ToObject<List<Item>>(jsonString).First(x => x.Id == itemID);
    }

    void Start()
    {
        var item = JsonToItem("/Scripts/inventory.json", 1);
    }
}