Search Unity

JsonUtilities.ToJson with List<string> not working as expected

Discussion in 'Scripting' started by juiced12, Aug 5, 2019.

  1. juiced12

    juiced12

    Joined:
    Nov 8, 2018
    Posts:
    44
    Hi guys,

    I'm trying to serialize a list using JsonUtilities.ToJson, however I'm running into a problem

    When I do the above, the string that it returns is just " {} "

    I don't understand why it's returning object, as far as I am aware, ToJson should serialize lists no problem.

    My code looks like this


    //My list of type string
    public List<string> saveList = new List<string>();

    //First converting my data to a string. This is serialized correctly, and is as expected
    string newStringToSave = JsonUtility.ToJson(Data);

    //Add string to list
    saveList.Add(newStringToSave);

    //Returns {}
    string result = JsonUtility.ToJson(saveList);


    Anyone have any ideas what could be going wrong here?

    EDIT:
    I've also tried wrapping the string in a custom class with the Serializable attribute and then add that to the list. Still no luck. Honestly can't figure out where I'm going wrong
     
    Last edited: Aug 5, 2019
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    Alas the Unity JSON utility cannot do this. See here: https://docs.unity3d.com/Manual/JSONSerialization.html

    I quote from the above:

    "The JSON Serializer does not currently support working with ‘unstructured’ JSON (i.e. navigating and editing the JSON as an arbitrary tree of key-value pairs). If you need to do this, you should look for a more fully-featured JSON library."

    Countless developer hours have been squandered discovering this completely unobvious limitation. The Unity JSON "implementation" should never have been named anything but "LITE" or "Primitive" in its title, since everybody naturally expects generic collections would be serialized.

    Instead I recommend checking out the Newtonsoft JSON package available for free on the Unity asset store:

    https://assetstore.unity.com/packages/tools/input-management/json-net-for-unity-11347

    Sorry you were the latest time-waste victim. :/
     
  3. juiced12

    juiced12

    Joined:
    Nov 8, 2018
    Posts:
    44
    Ah I see. Haha only lost a few hours. More than enough though!. Thanks Kurt. Will take a look at this library now!
     
    Kurt-Dekker likes this.
  4. juiced12

    juiced12

    Joined:
    Nov 8, 2018
    Posts:
    44
    Thanks for recommending the package. It's excellent. Reverting my code back to what I originally thought would work and sure enough no problems!
     
    Kurt-Dekker likes this.
  5. transporter_gate_studios

    transporter_gate_studios

    Joined:
    Oct 17, 2016
    Posts:
    219
    So if i had a monobehaviour class containing several Lists<> of different custom types that i wrote (and added Serializable to) and attempted to write that whole class to json with Kurt's recommended utility it would work?

    i guess im a bit confused by this:

    "Note: GameObjects and MonoBehaviors cannot be serialized directly as well as some built in classes (such as Texture2D that doesn't have a public parameterless constructor) but they are simple to serialize using either proxy classes or creating a custom ContractResolver or JsonConverter. JSON .NET is super extensible."

    but if i stick all this stuff in a custom class im good right?

    I have lots of custom classes that mix Unity stuff like Sprites with my classes and i'm wondering where its gunna break.
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    No sense wondering, you can test.

    You will not be able to serialize references to Unity things like Sprites, generally speaking.

    When loading, you can never re-create a MonoBehaviour or ScriptableObject instance directly from JSON. The reason is they are hybrid C# and native engine objects, and when the JSON package calls
    new
    to make one, it cannot make the native engine portion of the object.

    Instead you must first create the MonoBehaviour using AddComponent<T>() on a GameObject instance, or use ScriptableObject.CreateInstance<T>() to make your SO, then use the appropriate JSON "populate object" call to fill in its public fields.

    Some more Load/Save notes and steps:

    https://forum.unity.com/threads/save-system-questions.930366/#post-6087384

    Don't use the binary formatter/serializer: it is insecure, it cannot be made secure, and it makes debugging very difficult, plus it actually will NOT prevent people from modifying your save data on their computers.

    https://docs.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide
     
    gnurk likes this.
  7. transporter_gate_studios

    transporter_gate_studios

    Joined:
    Oct 17, 2016
    Posts:
    219
    well for the references like prefabs, textures, sprites i can use [ignorejson] because i store all my refs as strings and load with addressables. Do you know if a serializing nested lists (class with a list of lists of lists) is possible?
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    ...

    If a rando on the forum says 'yes' or 'no' and is incorrect, has anyone benefitted?

    Test!
     
    Bunny83 likes this.
  9. AdionN

    AdionN

    Joined:
    Nov 6, 2019
    Posts:
    16
    Example code for anyone who don't want to add extra asset. I will add extra level of data, so if you will need multiple objects or list you could still handle it. I hope it will help.

    Code (CSharp):
    1.  
    2.  [System.Serializable]
    3.  public class JSONWithListOfObjects
    4.  {
    5.         public string Date;
    6.         public List<CustomObject> spaceList;
    7.  }
    8.  
    9.  
    10. [System.Serializable]
    11.     public class CustomObject: ICloneable
    12.     {
    13.         public int Data1;
    14.         public string Data2;
    15.         public string Data3;
    16.  
    17.         public object Clone()
    18.         {
    19.             CustomObject space = new CustomObject
    20.             {
    21.                 Data1= Data1,
    22.                 Data2= Data2,
    23.                 Data3= Data3
    24.             };
    25.             return space;
    26.         }
    27.     }
    This is description of your object of save. Clone() is just used to clone data instead of link, so if data is changed frequently, it wouldn't be effected.

    Code (CSharp):
    1.          
    2. //lets create some random data
    3.             for (int i = 0; i < 10; i++) {
    4.             CustomObject SomeRandomList = new CustomObject() {
    5.                 Data1= i,
    6.                 Data2="looool" + i,
    7.                 Data3= "target" + i,
    8.             };
    9. //lets just create new object that we want to save as JSON
    10.             JSONWithListOfObjects writeObjectListJSON = new JSONWithListOfObjects();
    11. //lets add current time, for extra data
    12.             writeObjectListJSON.Date = DateTime.Now.ToString();
    13. //now lets put our random data list into our object we will print, next to data.
    14.             writeObjectListJSON.spaceList = new List<CustomObject>(SomeRandomList);
    15. //create string from the object
    16.             string writeToFile = JsonUtility.ToJson(writeObjectListJSON);
    17. //you can do whatever after, but for checking lets create file.
    18.             File.WriteAllText(Application.dataPath + "/MyJsonFile.json", writeToFile);
    19.  
    in the end you will get you string in form something like this:

    {"Date":"10/21/2021 3:07:14 PM","CustomObject":[{"Data1":0,"Data2":"looool0","Data3":"target0"},{"Data1":1,"Data2":"looool1","Data3":"target1"},{"Data1":2,"Data2":"looool2","Data3":"target2"...<..>}]}
     
  10. theonlysake

    theonlysake

    Joined:
    Dec 12, 2019
    Posts:
    4
    If you just need to wrap a List<> use this:

    Code (CSharp):
    1.  
    2. [System.Serializable]
    3. public class JsonableListWrapper<T>
    4. {
    5.     public List<T> list;
    6.     public JsonableListWrapper(List<T> list) => this.list = list;
    7. }
    8.  
    Then, using OPs problem as an example, they could just do this:

    Code (CSharp):
    1.  
    2. List<string> stringList = new List<string>(){"one","two","three"};
    3. // To Json
    4. string stringListAsJson = JsonUtility.ToJson(new JsonListWrapper<string>(stringList));
    5. // From Json
    6. List<string> stringListFromJson = JsonUtility.FromJson<JsonListWrapper<string>>(stringListAsJson).list;
    7.  
    This is what I use to save/load user configured input to/from PlayerPrefs.
     
  11. akuno

    akuno

    Joined:
    Dec 14, 2015
    Posts:
    88
    That just worked, thanks.
    Now, why do we need to wrap it?
     
  12. thephoenix007

    thephoenix007

    Joined:
    Nov 29, 2018
    Posts:
    7
    Because unity can't be bothered to fix their crappy JSON utility. Across the board, unity's ability to handle any type of stored/structured data is just garbage, and probably always will be.
     
    Rodolfo-Rubens and breylinlee like this.
  13. LostShepherd

    LostShepherd

    Joined:
    May 31, 2019
    Posts:
    38
    Would this wrapper method work in a case where I have player info in a class but the class itself contains a List of more specific level info e.g.

    Code (CSharp):
    1. public class IG_CloudData
    2. {
    3.     public int version;
    4.     public string name;
    5.     public bool adsFree;
    6.     public List<PlayerLevelInfo> levels;
    7.     public int score;
    8.     public string currentLevel;
    9.     public string maxLevelUnlocked;
    10. }
    11.  
    12. public class PlayerLevelInfo
    13. {
    14.     private string _level;
    15.     private bool _status;
    16.     private string _bestTime;
    17.     private int _bestScore;
    18.     private int _stars;
    19.     private int _bestScore;
    20. }
    Here I'd like to serailise and deserialise IG_CloudData.

    Thanks in advance,
    LS
     
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    You already have the code typed in, make more code to test it.

    Be sure to let us know what you find.
     
    Olipool likes this.
  15. LethalInjection

    LethalInjection

    Joined:
    Jan 25, 2015
    Posts:
    37
    Last edited: Feb 4, 2024
    Ryiah likes this.
  16. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,155
  17. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,861
  18. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 5, 2024
    Posts:
    473
    I do not recommend Odin Serializer anymore. Unfortunately it seems it doesn't work with .Net Standard, only with .Net Framework. Which is limiting its use inside Unity.
     
  19. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,861
    I've only really used it as part of the overarching Odin Inspector package, where it comes as pre-compiled dlls, so I wasn't aware of these issues.