- Home /
JsonUtility.ToJson returns empty file
Hello I hae this script that should sae data about GameObjects to a json file:
public void SaveData()
{
List<string> jsonStrings = new List<string>();
foreach (GameObject sceneObject in toolbar.sceneObjects)
{
string name = sceneObject.name;
jsonStrings.Add(name);
string tag = sceneObject.tag;
jsonStrings.Add(tag);
Vector3 position = sceneObject.transform.position;
jsonStrings.Add(position.ToString());
Vector3 rotation = sceneObject.transform.eulerAngles;
jsonStrings.Add(rotation.ToString());
Vector3 scale = sceneObject.transform.localScale;
jsonStrings.Add(scale.ToString());
Component meshFilter = sceneObject.GetComponent<MeshFilter>();
jsonStrings.Add(meshFilter.ToString());
Component Renderer = sceneObject.GetComponent<Renderer>();
jsonStrings.Add(Renderer.ToString());
Component collider = sceneObject.GetComponent<Collider>();
jsonStrings.Add(collider.ToString());
Component rigidbody = sceneObject.GetComponent<Rigidbody>();
jsonStrings.Add(rigidbody.ToString());
Component light = sceneObject.GetComponent<Light>();
foreach(MonoBehaviour script in sceneObject.GetComponents<MonoBehaviour>())
{
string scriptName = script.name;
jsonStrings.Add(scriptName);
if(script.name == "ChildObject")
{
GameObject extender = sceneObject.GetComponent<ChildObject>().childObject;
jsonStrings.Add(extender.ToString());
}
if(script.name == "Portal")
{
Vector3 coordinates = sceneObject.GetComponent<Portal>().coordinates;
jsonStrings.Add(coordinates.ToString());
GameObject player = sceneObject.GetComponent<Portal>().player;
jsonStrings.Add(player.ToString());
}
if(script.name == "YPad")
{
bool touching = sceneObject.GetComponent<YPad>().touching;
jsonStrings.Add(touching.ToString());
AudioClip sound = sceneObject.GetComponent<YPad>().launch;
jsonStrings.Add(sound.ToString());
}
if(script.name == "SetPlayerPrefs")
{
int thislevel = sceneObject.GetComponent<SetPlayerPrefs>().ThisLevel;
jsonStrings.Add(thislevel.ToString());
int thislevelmaxscore = sceneObject.GetComponent<SetPlayerPrefs>().ThisLevelMaxScore;
jsonStrings.Add(thislevelmaxscore.ToString());
GameObject player = sceneObject.GetComponent<SetPlayerPrefs>().Player;
jsonStrings.Add(player.ToString());
}
}
}
string jsonData = JsonUtility.ToJson(jsonStrings, true);
File.WriteAllText(localLevelLocation + "/" + properties.levelName + "_data.json" , jsonData);
}
But the file that is created is empty except of two curly brackets
Answer by upasnavig90 · Feb 15, 2018 at 11:13 AM
HI, please try using a model class, where you take all the fields required and use the object of that class for parsing in json.
you can look at this for reference:
[System.Serializable]
public class ToolBar {
public string hello;
public string hi;
}
ToolBar toolBar;
void Awake()
{
SaveData ();
}
public void SaveData()
{
toolBar = new ToolBar ();
toolBar.hello="Hello";
toolBar.hi = "hi";
string jsonData = JsonUtility.ToJson(toolBar, true);
File.WriteAllText(Application.dataPath+"/" + "_data.json" ,
jsonData);
}
}
Note: if you want to save the whole scene, its better to use assetBundle or scriptable objects.
Answer by Bunny83 · Feb 15, 2018 at 12:26 PM
You have two main problems here. First Unity's JsonUtility only supports an object as root element. However you pass a string array. Second the way you try to "pack" your data makes not much sense. You just convert every thing to strings and create one big linear array of strings.
Also serializing something like jsonStrings.Add(Renderer.ToString());
would just save the class name of the renderer, nothing else. The same is true for your collider, meshfilter, rigidbody and light. You also iterate through all monobehaviour components adding more unstructured data in arbitrary order. In short the data inside your string array is pretty useless since it doesn't really contain the necessary data nor does it contain it in a way it can be read again in a meaningful way.
You would need to do two main changes. First of all you have to actually save meaningful information that is required to restore the current state. Store the information in a structured way.
Keep in mind that JSON has no built-in way to serialize cross references between objects. For example for your "Portal" script you seem to try to save a reference to another gameobject (the player). As i already said currently you just store the name of the gameobject which however can be ambiguous. If the object you want to reference is actually saved as well you can simply serialize the InstanceId of each object and just use that Id to reference other objects within the same save file. Note that the instanceId can not be used to reference objects between different sessions. So the data need to be self-contained. When you recreate or load the same data again each object will have different instance ids.
Unity's JsonUtility isn't really suited for serializing complete gameobjects structures. It's also quite limited as it can only object map your data. So you would need to pack all your data into custom objects that can be serialized by Unity (as upasnavig90 said). To actually save a whole scene the JsonUtility is not a good choice. If you like to save and restore your data in a manual approach you may want to use a more flexible JSON framework like my SimpleJSON ^^.
With the files "SimpleJSON.cs" and "SimpleJSONUnity.cs" you can do something like:
public void SaveData()
{
JSONObject root = new JSONObject();
JSONArray objs = root["sceneObjects"].AsArray();
foreach (GameObject sceneObject in toolbar.sceneObjects)
{
JSONObject obj = objs[-1].AsObject();
obj["name"] = sceneObject.name;
obj["tag"] = sceneObject.tag;
obj["id"] = sceneObject.GetInstanceID();
JSONArray components = obj["components"].AsArray();
foreach(Component comp in sceneObject.GetComponents<Component>())
{
JSONObject jcomp = components[-1].AsObject();
string type = comp.GetType().Name;
jcomp["type"] = type;
jcomp["id"] = comp.GetInstanceID();
if (type == "Transform")
{
Transform t = (Transform)comp;
jcomp["position"] = t.position;
jcomp["rotation"] = t.eulerAngles;
jcomp["scale"] = t.localScale;
if (t.parent != null)
jcomp["parent"] = t.parent.GetInstanceID();
}
else if (type == "BoxCollider")
{
BoxCollider c = (BoxCollider)comp;
jcomp["center"] = c.center;
jcomp["size"] = c.size;
jcomp["isTrigger"] = c.isTrigger;
}
else if (type == "SphereCollider")
{
SphereCollider c = (SphereCollider)comp;
jcomp["center"] = c.center;
jcomp["radius"] = c.radius;
}
// [ ... ]
}
}
File.WriteAllText(localLevelLocation + "/" + properties.levelName + "_data.json" , root.ToString(3));
}
Note this is just an example. Also there are several edge cases. Creating a whole scene from scratch would mean you have to serialize everything (literally EVERYTHING). So a MeshFilter will reference a Mesh object, so you have to serialize the Mesh as well. A MeshRenderer references a material, so the material need to be serialized. A material might reference a texture so you need to serialize the texture. At some point you may want / need to reference actual assets in your game instead of storing it along in the save game file. However this complicates things a bit since you need a way to uniquely identify those assets. Things which you usually want to reference would be materials, meshes, textures, audioclips, ...
That's why saving a whole scene isn't an easy task and hasn't been solved in a single "once-and-for-all" solution. Most savegames would be customized for the exact purpose. For example instead of recreating the whole gameobjects from scratch most dynamic scenes rely on prefabs. Though that means you have to remember which prefab was used to create a certain object. Also, same problem as above, you have to have a way to uniquely identify each prefab.
Your answer
Follow this Question
Related Questions
Transfer data to/from server in mobile 0 Answers
[Serialize] Attribute with JSON 1 Answer
Don't Destroy on Load Object and GameData 1 Answer
Problem when saving and loading 1 Answer
Why are JSON Libraries even necessary? Isn't it Native? 1 Answer