Search Unity

List.Contains issue when deserializing from Json to List<>

Discussion in 'Scripting' started by Flynn_Prime, Aug 19, 2017.

  1. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    In the following code I check to see if an item exists in my inventory list, and if not then add the item. This works for the most part, except for when I exit and press play again. When my list is loaded from Json inventory.Contains is always returning false, even if the item does exist. But if I try to add the same item twice without exiting the game first I get the Debug.Log ("Item already owned") correctly. What could be causing this issue?

    P.S The deserialized list is working, and I can access the list elements and debug them accordingly so I don't think this is where the problem lies.

    Code (CSharp):
    1. private void Awake ()
    2.     {
    3.         itemDatabase = GameObject.FindGameObjectWithTag ("ItemDatabase").GetComponent<ItemDatabase> ();
    4.  
    5.         if (File.Exists (Application.dataPath + "/inventory.json"))
    6.         {
    7.             LoadInventory ();
    8.         } else
    9.         {
    10.             inventory = new List<Item> ();
    11.         }
    12.     }
    13.  
    14.     public void LoadInventory ()
    15.     {
    16.         var settings = new JsonSerializerSettings ();
    17.         settings.TypeNameHandling = TypeNameHandling.Auto;
    18.  
    19.         string json = File.ReadAllText (Application.dataPath + "/inventory.json");
    20.         inventory = JsonConvert.DeserializeObject<List<Item>> (json, settings);
    21.  
    22.         foreach (Item i in inventory)
    23.         {
    24.             i.itemIcon = Resources.Load<Sprite> ("Items/Icons/" + i.itemSlug);
    25.             i.itemModel = Resources.Load<GameObject> ("Items/Models/" + i.itemSlug);
    26.         }
    27.     }
    28.  
    29.     public void SaveInventory ()
    30.     {
    31.         var settings = new JsonSerializerSettings ();
    32.         settings.TypeNameHandling = TypeNameHandling.Auto;
    33.         settings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    34.  
    35.         string json = JsonConvert.SerializeObject (inventory, Formatting.Indented, settings);
    36.         File.WriteAllText (Application.dataPath + "/inventory.json", json);
    37.     }
    38.    
    39.     public void AddItemToInventoryList (int id)
    40.     {
    41.         Item itemToAdd = itemDatabase.GetItemByID (id); //Grab item from DB
    42.  
    43.         if (!inventory.Contains (itemToAdd)) //Always returning false IF the item pre-exists in Json
    44.         {
    45.             inventory.Add (itemToAdd);
    46.             SaveInventory ();
    47.         } else
    48.         {
    49.             Debug.Log ("Item already owned!");
    50.         }
    51.     }
     
    Last edited: Aug 20, 2017
  2. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
  3. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    It's a reference equality issue. The items in your item database are the same reference when you serialize them, but when deserialization happens, Unity has no way of knowing the items it's deserializing in your inventory are supposed to be references to ones in your item database. Instead, it creates a new object, which will no longer be equal to the one in your item database, even though the values are the same. Rather than using Contains, which checks for reference equality, you need to check to see if your list contains an item with the same ID.
     
  4. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Thank you, that clears that up. How would I check that the list contains an item of the same ID without using List.Contains? I'm having the same reference equality issue when trying to remove an item from a list after it gets loaded via json, so if I can fix this I imagine I can work somethings similar for my other bugs.

    EDIT: After playing around this is what I came up with. Not sure if this is 'good code' but it works for now... Thank you for your help!

    Code (CSharp):
    1.     public void AddItemToInventoryList (int id)
    2.     {
    3.         Item itemToAdd = itemDatabase.GetItemByID (id); //Grab item from DB
    4.  
    5.         if (inventory.Count != 0)
    6.         {
    7.             bool exists = false;
    8.            
    9.             for (int i = 0; i < inventory.Count; i++)
    10.             {
    11.                 if (inventory [i].itemID == itemToAdd.itemID)
    12.                 {
    13.                     exists = true;
    14.                     Debug.Log ("Item already owned!");
    15.                 }
    16.             }
    17.  
    18.             if (exists == false)
    19.             {
    20.                 inventory.Add (itemToAdd);
    21.                 AddSingleItem (itemToAdd);
    22.             }
    23.         } else if (inventory.Count == 0)
    24.         {
    25.             inventory.Add (itemToAdd);
    26.             AddSingleItem (itemToAdd);
    27.         }
    28.  
    29.         SaveInventory ();
    30.     }
     
    Last edited: Aug 20, 2017
  5. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    You could also use LINQ's Any() method:

    Code (csharp):
    1. using System.Linq;
    2. //......stuff
    3. public void AddItemToInventoryList (int id)
    4. {
    5.     Item itemToAdd = itemDatabase.GetItemByID (id); //Grab item from DB
    6.  
    7.     if (!inventory.Any(item => item.itemID == itemToAdd.itemID))
    8.     {
    9.         inventory.Add (itemToAdd);
    10.         SaveInventory ();
    11.     } else
    12.     {
    13.         Debug.Log ("Item already owned!");
    14.     }
    15. }
     
  6. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    You could also use List.Find with a predicate to reduce it to one line:
    Code (csharp):
    1. if (inventory.Find(x => x.itemID == id) != null)
     
  7. Flynn_Prime

    Flynn_Prime

    Joined:
    Apr 26, 2017
    Posts:
    387
    Thanks guys. Is there a notable performance difference between Linq and unitys functions?
     
  8. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Profile it and find out! Super easy, and a good habit to get into.