Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Howto use JSON in 5.3 [Guide]

Discussion in 'Immediate Mode GUI (IMGUI)' started by Ippokratis, Dec 12, 2015.

  1. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    Hi,
    One of the new additions of Unity 5.3 is the introduction of the JSONUtility.
    You can find the relative documentation here : https://docs.unity3d.com/Manual/JSONSerialization.html
    In this post, I will show a simple implementation.
    First, we will create a Scriptable Object called TestData to store the data from our class.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. #if UNITY_EDITOR
    5. using UnityEditor;
    6. #endif
    7.  
    8. [System.Serializable]
    9. public class TestData : ScriptableObject
    10. {
    11.     public string curDataStorage;
    12. }
    13.  
    Next, I 'll create a simple class called Test. As long as you are using fields, your own custom classes should work too.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Test : MonoBehaviour
    5. {
    6.     public TestData curData = null;
    7.  
    8.     public float nr0 = 0f;
    9.     public float nr1 = 1f;
    10.     public float nr2 = 2f;
    11. }
    12.  
    Now, create a folder called Editor and place the next script called TestEditor inside.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEditor;
    4. using System.Collections;
    5.  
    6. [CustomEditor(typeof(Test))]
    7. //
    8. public class TestEditor : Editor
    9. {
    10.     Test targetTest;
    11.     string path = "Assets/DataAsset.asset";
    12.    
    13.     void OnEnable ()
    14.     {
    15.         targetTest = (Test)target;
    16.         if(targetTest!=null)
    17.         {
    18.             targetTest.curData = LoadData();
    19.         }
    20.     }
    21.  
    22.     void OnDestroy()
    23.     {
    24.         SaveData();
    25.     }
    26.    
    27.     public override void OnInspectorGUI ()
    28.     {
    29.         DrawDefaultInspector();
    30.  
    31.         EditorApplication.playmodeStateChanged = () =>
    32.         {
    33.             if( EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isPlaying )
    34.             {
    35.                 SaveData();
    36.             }
    37.         };
    38.  
    39.         EditorUtility.SetDirty(targetTest);
    40.     }
    41.  
    42.     void SaveData()
    43.     {
    44.         TestData curData =  (TestData)AssetDatabase.LoadAssetAtPath(path, typeof(TestData));
    45.         if(curData==null)
    46.         {
    47.             curData = CreateDatabase();  
    48.         }
    49.         curData.curDataStorage = JsonUtility.ToJson(targetTest);
    50.         AssetDatabase.Refresh();
    51.         AssetDatabase.SaveAssets();  
    52.     }
    53.    
    54.     TestData LoadData()
    55.     {
    56.         TestData curData =  (TestData)AssetDatabase.LoadAssetAtPath(path, typeof(TestData));
    57.        
    58.         if(curData!=null)
    59.         {
    60.             JsonUtility.FromJsonOverwrite( curData.curDataStorage, targetTest );
    61.             return curData;
    62.         }
    63.         else
    64.         {
    65.             return CreateDatabase();  
    66.         }
    67.     }
    68.    
    69.     TestData CreateDatabase()
    70.     {
    71.         TestData curData = (TestData)ScriptableObject.CreateInstance(typeof(TestData));
    72.         if(curData!=null)
    73.         {
    74.             AssetDatabase.CreateAsset(curData, path);
    75.             AssetDatabase.Refresh();
    76.             AssetDatabase.SaveAssets();
    77.             return curData;
    78.         }
    79.         else
    80.         {
    81.             return null;
    82.         }
    83.     }
    84. }
    85.  
    Every change of the class values after you hit Play, persists in Edit mode.
    Kind regards,
    -Ippokratis
     
    Last edited: Dec 12, 2015
    pluMmet and flashframe like this.
  2. trelobyte

    trelobyte

    Joined:
    Nov 17, 2010
    Posts:
    54
    This is so cool and helpful thanks Ippokratis!
     
  3. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    Thanks Bill,
    Here is another example, for using it with an Editor Window this time.

    Create a folder called Editor and place the following script named ExEditorWindow inside :
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Collections;
    4.  
    5. namespace ExNamespace
    6. {
    7.  
    8. [System.Serializable]
    9. public class ExEditorWindow : EditorWindow
    10. {
    11.     [MenuItem("Window/Example Editor Window", false, 10)]
    12.     public static void LaunchExEditorWindow()
    13.     {
    14.         var exEditorWindow = EditorWindow.GetWindow<ExEditorWindow>("Example",true, typeof(EditorWindow));
    15.     }
    16.    
    17.     void OnEnable()
    18.     {
    19.         EditorApplication.playmodeStateChanged -= OnPlayModeChanged;
    20.         EditorApplication.playmodeStateChanged += OnPlayModeChanged;
    21.         curData = LoadData();
    22.     }
    23.  
    24.     void OnDisable()
    25.     {
    26.         EditorApplication.playmodeStateChanged -= OnPlayModeChanged;
    27.         SaveData();
    28.     }
    29.    
    30.     void OnPlayModeChanged()
    31.     {
    32.         if (EditorApplication.isPlayingOrWillChangePlaymode)
    33.         {
    34.             SaveData();
    35.         }
    36.     }
    37.  
    38.     void OnDestroy()
    39.     {
    40.         EditorApplication.playmodeStateChanged -= OnPlayModeChanged;
    41.         SaveData();
    42.     }
    43.    
    44.     public ExData curData;
    45.     string path = "Assets/ExDataAsset.asset";
    46.     public Color exCol = Color.white;
    47.    
    48.     void OnGUI()
    49.     {
    50.         EditorGUILayout.BeginHorizontal();
    51.         GUILayout.FlexibleSpace();
    52.         GUILayout.Label("Example Editor Window",  GUILayout.Height(40), GUILayout.ExpandWidth(true));
    53.         GUILayout.FlexibleSpace();
    54.         EditorGUILayout.EndHorizontal();
    55.         exCol = EditorGUILayout.ColorField("Example color", exCol);
    56.         //Update the UI every frame
    57.         Repaint();    
    58.     }
    59.    
    60.     void SaveData()
    61.     {
    62.         curData =  (ExData)AssetDatabase.LoadAssetAtPath(path, typeof(ExData));
    63.         if(curData==null)
    64.         {
    65.             curData = CreateDatabase();  
    66.         }
    67.         curData.curDataStorage = JsonUtility.ToJson(this);
    68.         AssetDatabase.Refresh();
    69.         AssetDatabase.SaveAssets();  
    70.     }
    71.    
    72.     ExData LoadData()
    73.     {
    74.         curData =  (ExData)AssetDatabase.LoadAssetAtPath(path, typeof(ExData));
    75.        
    76.         if(curData!=null)
    77.         {
    78.             JsonUtility.FromJsonOverwrite( curData.curDataStorage, this );
    79.             return curData;
    80.         }
    81.         else
    82.         {
    83.             return CreateDatabase();  
    84.         }
    85.     }
    86.    
    87.     ExData CreateDatabase()
    88.     {
    89.         ExData curData = (ExData)ScriptableObject.CreateInstance(typeof(ExData));
    90.         if(curData!=null)
    91.         {
    92.             AssetDatabase.CreateAsset(curData, path);
    93.             AssetDatabase.Refresh();
    94.             AssetDatabase.SaveAssets();
    95.             return curData;
    96.         }
    97.         else
    98.         {
    99.             return null;
    100.         }
    101.     }
    102. }
    103.  
    104. }//end namespace ExNamespace
    Now create another script called ExData.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. #if UNITY_EDITOR
    5. using UnityEditor;
    6. #endif
    7.  
    8. namespace ExNamespace
    9. {
    10.  
    11. [System.Serializable]
    12. public class ExData : ScriptableObject
    13. {  
    14.     public string curDataStorage;
    15. }
    16.  
    17. }//end namespace ExNamespace
    This can serve as a base for your Editor Windows.
     
  4. JohnRyanAudio

    JohnRyanAudio

    Joined:
    Jan 20, 2013
    Posts:
    7
    First of all, thanks for the guide. I've taken your original example and tweaked it in a test project to fit my project's use case. I'm seeing the data persist across scenes in the editor, but not until I click the object in the hierarchy.
    I'm fairly new to Editor scripting, but I think I'm making the connection on why this behavior is happening.

    However, I'm looking for a solution that updates the persisted data at run-time. Is this possible using what you've laid out here?

    Thanks in advance.
     
  5. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    When you are in editor mode, you can apply changes only after you select the gameObject. This is expected, I do not know how else you may approach it.

    If you need to make changes in play mode that persist, you need to move the save and load data methods from the TestEditor script to the Test script and call it OnEnable, OnDisable, OnDestroy - plus whatever else you need to do for your particular use case.

    Share your findings here, if you wish.
     
  6. charlie-peter

    charlie-peter

    Joined:
    Dec 14, 2015
    Posts:
    9
    Hi,
    Great guide got it working fine with simple objects and even vectors. The only thing I'm running into is trouble getting an Array/List to parse through the built in Unity Json. I encapsulated it in a struct (Like the Unity page said.) and it still didn't work. Is it just not implemented in Unity yet?

    example pseudo code.
    struct arrayHolder
    {
    object[] arrayOfObjects;
    }
    void Save()
    {
    arrayHoler temp;
    //fill up the array with objects;
    string fileFiller = JsonUtility.ToJson(temp);
    WriteFile(path, fileFiller);
    }
     
  7. drussilla

    drussilla

    Joined:
    Mar 20, 2014
    Posts:
    9
    Also, there is a problem with System.Guid. You cannot serialize\deserialize this type. But there you can also create wrapper class:
    Code (CSharp):
    1. [Serializable]
    2. public struct SerializableGuid
    3. {
    4.     public string Value;
    5.     private SerializableGuid(string value)
    6.     {
    7.         Value = value;
    8.     }
    9.     public static implicit operator SerializableGuid(Guid guid)
    10.     {
    11.         return new SerializableGuid(guid.ToString());
    12.     }
    13.     public static implicit operator Guid(SerializableGuid serializableGuid)
    14.     {
    15.         return new Guid(serializableGuid.Value);
    16.     }
    17. }
    Advanced version of this wrapper
     
    RMGK likes this.
  8. pluMmet

    pluMmet

    Joined:
    Jul 18, 2015
    Posts:
    133
    This is a great start Thank You.

    The only thing I'd like to know how to do on top of this is to be able to nest the exposed fields.

    So in your OP example Nr 0 would be a number and have a bunch of other data nested to that number.

    So if I have example 5 objects that share the same info but variations, those variations would then be stored under
    (Nr 0 - Nr 4)

    Nr 1
    which weapon is he carrying
    is guy wearing a hat
    is guy wearing a red or blue shirt

    Nr 2
    which weapon is he carrying
    is guy wearing a hat
    is guy wearing a red or blue shirt ;)
     
    Last edited: Jun 18, 2016
  9. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
    You can try to modify the above code to your class specs and see how it works.
    It is easier to comment on code :)
     
  10. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,222
    I'm trying to convert a Stack<SavedData> to json, your api takes it without error message but returns {}...
    Any hint?
    (I tried converting the stack to a list, which is serialized by unity and I still get {})

    Code (CSharp):
    1.     [System.Serializable]
    2.     public class SavedData{
    3.         public string entityID;
    4.         public string prefabID;
    5.         public Vector3 position;
    6.         public Quaternion rotation;
    7.         public Panda.BTSnapshot snapshot = null;
    8.     }
    9.  
    10.     void Save()
    11.     {
    12.         Stack<SavedData> save = new Stack<SavedData> ();
    13.         foreach (KeyValuePair<GameObject, GameObject> g in Pool.poolInstantiated)
    14.         {
    15.             SavedData sd = new SavedData ();
    16.             sd.entityID = g.Key.name;
    17.             sd.prefabID = g.Value.name;
    18.             sd.position = g.Key.transform.position;
    19.             sd.rotation = g.Key.transform.rotation;
    20.             PandaBehaviour pb = g.Key.GetComponent<PandaBehaviour> ();
    21.             if (pb) sd.snapshot = pb.snapshot;
    22.             save.Push (sd);
    23.         }
    24.         string savedString = JsonUtility.ToJson (save);
    25.         PlayerPrefs.SetString (SAVEGAMENAME, savedString);
    26.         hasSaveGame = true;
    27.     }
    here is what debug has to say

    Screen Shot 2016-09-24 at 10.06.27 PM.png
     
    Last edited: Sep 25, 2016
  11. flashframe

    flashframe

    Joined:
    Feb 10, 2015
    Posts:
    789
    Did you try creating a wrapper class for the Stack object? Something like this usually works for me:

    Code (CSharp):
    1. [System.Serializable]
    2.     public class SavedData{
    3.         public string entityID;
    4.         public string prefabID;
    5.         public Vector3 position;
    6.         public Quaternion rotation;
    7.         public Panda.BTSnapshot snapshot = null;
    8.     }
    9.  
    10.     [System.Serializable]
    11.     public class SavedDataStack{
    12.         public Stack<SavedData> dataStack;
    13.     }
    14.  
    15.     void Save()
    16.     {
    17.  
    18.         SaveDataStack save = new SavedDataStack();
    19.  
    20.         foreach (KeyValuePair<GameObject, GameObject> g in Pool.poolInstantiated)
    21.         {
    22.             SavedData sd = new SavedData ();
    23.             sd.entityID = g.Key.name;
    24.             sd.prefabID = g.Value.name;
    25.             sd.position = g.Key.transform.position;
    26.             sd.rotation = g.Key.transform.rotation;
    27.             PandaBehaviour pb = g.Key.GetComponent<PandaBehaviour> ();
    28.             if (pb) sd.snapshot = pb.snapshot;
    29.             save.dataStack.Push (sd);
    30.         }
    31.         string savedString = JsonUtility.ToJson (save);
    32.         PlayerPrefs.SetString (SAVEGAMENAME, savedString);
    33.         hasSaveGame = true;
    34.     }
     
  12. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,222
    JsonUtility.ToJson still returns "{}" because stack isn't serialized by unity. I replaced by list and pop() by [0] and removeat(0) and it's now working.
     
    Last edited: Sep 26, 2016
  13. Abbubakar

    Abbubakar

    Joined:
    Apr 21, 2016
    Posts:
    2
    how can i get url data and show in unity as a grid any one can help me