Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Discussion simple self-contained c# script for reading and writing a json file

Discussion in 'Scripting' started by JeffreyVentrella, Sep 21, 2023.

  1. JeffreyVentrella

    JeffreyVentrella

    Joined:
    Jun 16, 2016
    Posts:
    12
    Long Preamble + Caveat:

    On the one hand: there's nothing like a small script that performs a desired task in Unity that can be dropped in to one's project, and it just works. On the other hand: this is not always easy or straightforward because there is rarely just one way of doing things, and there are many kinds of needs that people have.

    But there may be others like me who just want to get started with a simple (but "complete") example of a particular feature and then take it from there. Once I know the basic components, I'm sure it will look easy in retrospect. This applies to other issues, perhaps even more than this one (e.g., TextMesh!) Well anyway, here's my question:

    Are there any examples of a "simple but complete" script that reads a small json file, makes a small change, and then reads it back to a file? (and includes any required "using" stuff at the top, etc). This may already have been provided but I haven't found one yet, and I guess I'm not so good at rummaging for these things. I have found some examples, and could cobble this together, but I'm really interested in process in general.

    For instance, I know that serialization is necessary (or is it just important?) and it's handy to understand its purpose, but that can wait. Getting something to work is often a first step in the comprehension process, and sometimes comprehension is not really the goal. Tutorials are wonderful, but they take time.

    I often request a "simple but complete" solution on this forum, but I know this cannot be clearly defined. What I usually mean is that it's easier for me (and I assume others as well, but I may be wrong) to not have extra scaffolding, helper functions, or irrelevant bits (as generous as they may be in intent), because they constitute noise in the signal, and I prefer to design the contextual glue myself.

    I expect some pushback on this point along the lines of "it depends", and I would have to agree with that! (One man's noise is another man's signal).

    (The time it took me to write this might be longer than the time it would have taken to dig out an answer. So go figure).

    Sorry for the long question! I'm sometimes misunderstood with what are intentionally naive questions, so I just wanted to give some context. I posted a question recently in what I thought was a Discussion area, where I assumed people can just yak about process in general, not necessarily looking for specific tech solutions. So, this may be partly an issue of me not really knowing how to use this (mostly) awesome Forum.

    Thanks!
    -j
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    You mean something like my SimpleJSON? It doesn't depend on anything. It still has some additional "modules" you can simply drop next to the main file which contain extensions for various things. There's a Unity specific extension so you can directly convert an json object or array to a Vector2/3/4 and some other Unity types. There's also an extension for some of the .NET primitive types, so it's easier to work with.

    The whole framework is designed to be easily extensible. So you can implement your own conversion methods for your own types if you like. Though SimpleJSON by itself is just a JSON framework that is not an object mapper but simply reads the json into specialized types to directly represent the json structure.
     
  3. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    An "everything in one script" example is sometimes just impossible just for technical reasons, or at the very least would lead to one helluva big hunk of code that's hard to read, or will have to be heavily truncated of potentially useful information.

    That said a read/write script example could be pretty simple, though wouldn't be particularly informative:
    Code (CSharp):
    1. using System.IO;
    2. using UnityEngine;
    3.  
    4. public sealed class SerializationExample : Monobehavior
    5. {
    6.     [SerializeField]
    7.     private DataClass _dataClass;
    8.  
    9.     private static string Path => Application.persistentDataPath + "\data.txt";
    10.  
    11.     [ContextMenu("Write Data")]
    12.     private void WriteData()
    13.     {
    14.         string json = JsonUtility.ToJson(_dataClass, true);
    15.         File.WriteAllText(Path, json);
    16.     }
    17.  
    18.     [ContextMenu("Read Data")]
    19.     private void ReadData()
    20.     {
    21.         string json = File.ReadAllText(Path);
    22.         _dataClass = JsonUtility.FromJson<DataClass>(json);
    23.      
    24. #if UNITY_EDITOR
    25.         UnityEditor.EditorUtility.SetDirty(this);
    26. #endif
    27.     }
    28.  
    29.     [System.Serializable]
    30.     private class DataClass
    31.     {
    32.         public int SomeInt = 10;
    33.         public float SomeFloat = 1.5f;
    34.         public string SomeString = "Hello";
    35.     }
    36. }
    Here it is for what it's worth. Written in notepad++, so might have some errors.
     
    Last edited: Sep 22, 2023
    JeffreyVentrella likes this.
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    He he he... your Windows is showing. :)

    Screen Shot 2023-09-21 at 5.32.52 PM.png
     
    JeffreyVentrella likes this.
  5. JeffreyVentrella

    JeffreyVentrella

    Joined:
    Jun 16, 2016
    Posts:
    12

    Hi Bunny83!

    Okay, so if I include your script in my project (I assume I can add it as a Script component to my main GameObject, right?), what are the calls I need to make in order to (1) read a json file (I already have a simple one ready in my Assets/Resources directory) and (2) write a text string I have composed to a json file?

    Much appreciated!
    -j
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    No, why do you even think that? You only attach behaviour scripts to gameobjects. Only classes derived from MonoBehaviour can be attached to gameobjects and the purpose of such components are to define / alter the behaviour of a gameobject. Reading and writing json data has absolutely nothing to do with gameobjects. My Json library does not even depend on Unity. It's a pure C# framework without any additional dependencies, not even Unity

    My library is about parsing json data. Where that comes from is pretty irrelevant. It simply requires your json text in a string variable. You can simply use
    JSONNode data = JSON.Parse(yourJsonText);
    to parse the json text into an actual C# object tree. You first said you want to read / write files. Though inside your parenthesis you said you have a file inside your Resources folder. Those are two completely different things. The resources folder only exists during development inside the editor. Assets inside the resources folder gets compiled into the asset database of Unity and those assets are read-only at runtime in a build game. So here it becomes a bit fuzzy what you actually want to do. You can read json text from a read-only text resource inside the Resources folder or you can read an actual file from somewhere else using normal System.IO.File routines. The easiest one is
    System.IO.File.ReadAllText(fileName)
    which reads the content of a file as one string.

    If you actually want to load your json from the resources folder, you would have to use Unity's Resources class and use Resources.Load with the resource name. This gives you a "TextAsset". That TextAsset has a "text" property which contains the content of the file. Though as I said this is read only.

    When it comes to writing files in a build game, the most common place is at the persistentDataPath. This is supported by most target platforms.

    I'm not sure what this actually means:
    Json is a structured data format, nothing more. A "json file" is simply a file that contains json. You can not add something to a file. You can rewrite / overwrite the file with new data.

    A common way to ship some json with your game is to simply use a text asset inside Unity that acts as default / fallback data in case the file inside the persistentDataPath does not exist yet. Your kept your question very general / vague, so you can not expect some kind of example that fits your usecase since your usecase is completely unknown.

    Anyways here's a general example usage

    Code (CSharp):
    1. // [ ... ]
    2. using SimpleJSON;
    3. using System.IO;
    4. // [ ... ]
    5.  
    6. public TextAsset defaultJson;
    7. void Start()
    8. {
    9.     string fileName = Path.Combine(Application.persistentDataPath, "MySaveFile.json");
    10.     string jsonText = "";
    11.     if (File.Exists(fileName))
    12.     {
    13.         jsonText = File.ReadAllText(fileName);
    14.     }
    15.     else if (defaultJson != null)
    16.     {
    17.         jsonText = defaultJson.text;
    18.         File.WriteAllText(fileName, jsonText); // write the default data to the persistent data path
    19.     }
    20.     else
    21.     {
    22.         Debug.LogError("File not found and no defaultJson assigned in the inspector");
    23.     }
    24.     JSONNode root = JSON.Parse(jsonText);
    25.     // use the parse json data
    26.     string engine = root["engine"];
    27.     Debug.Log("Engine: " + engine);
    28.     double num = root["someNum"];
    29.     Debug.Log("Some number: " + num);
    30.     foreach(JSONNode n in root["obj"]["childs"])
    31.     {
    32.         Debug.Log("Child: " + n["name"].Value + " Age: " + n["age"].AsInt);
    33.     }
    34.  
    35.     // Adding object to the childs array
    36.     var newChild = new JSONObject();
    37.     newChild["name"] = "New name";
    38.     newChild["age"] = 42;
    39.     root["obj"]["childs"].Add(newChild);
    40.  
    41.     // serialize the root node back into a compact json string:
    42.     jsonText = root.ToString();
    43.     // alternatively use an indent count in ToString to pretty format the json
    44.     //jsonText = root.ToString(4); // indent by 4 spaces
    45.  
    46.     // write the jsonText back to the file
    47.     File.WriteAllText(fileName, jsonText);
    48. }
    49.  
    This example would assume a json string like this:

    Code (CSharp):
    1. {
    2.     "engine": "Unity",
    3.     "someNum": 123.456,
    4.     "obj": {
    5.         "childs": [
    6.             {
    7.                 "name": "Gordon Freeman",
    8.                 "age" : 27
    9.             },
    10.             {
    11.                 "name": "Alyx Vance",
    12.                 "age" : 24
    13.             }
    14.         ]
    15.     }
    16. }
    If you need more specific help, you have to be more specific what you actually want to do. This framework is designed to make working with the json data easy without the need to define separate C# classes. You can directly add arbitrary data, even if certain keys don't exist yet. So doing

    Code (CSharp):
    1. root["newObj"]["someSubObj"]["arr"][-1]["name"] = "Test";
    would create all the necessary objects on the fly. So this would result in
    Code (CSharp):
    1.  
    2. {
    3.     "newObj": {
    4.         "someSubObj": {
    5.             "arr" : [
    6.                 {
    7.                     "name": "Test"
    8.                 }
    9.             ]
    10.         }
    11.     }
    12. }
    Since the library contains the full source code you can see for yourself how it works if you're interested.
     
  7. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    ps: If you also add the Unity extension file to your project, it can directly convert Vector3 values to / from a JSONNode when the node represents either an array of numbers or an object with components named x, y and z. So when you have json data that looks like this:
    Code (CSharp):
    1. {
    2.     "v1": [ 1, 2, 3],
    3.     "v2": {
    4.         "x": 4,
    5.         "y": 5,
    6.         "z": 6,
    7.     }
    8. }
    with the extension file you can simply do

    Code (CSharp):
    1. Vector3 v1 = root["v1"];
    2. Vector3 v2 = root["v2"];
    Though you can also use the ReadVector3 method and specify custom component names. like this:

    Code (CSharp):
    1. Vector3 v = root["v2"].ReadVector3("y", "z", "x"); // (5, 6, 4)
     
  8. JeffreyVentrella

    JeffreyVentrella

    Joined:
    Jun 16, 2016
    Posts:
    12
    Thank you spiny199. That worked!

    A few comments:

    I was specifically [not] asking for an "everything in one script" and I'm not sure how you might have interpreted that. However, you provided exactly what I asked for, which was [not] an "everything in one script", but "to-the-point" and complete.

    I had to shuffle a few things around, but I can take it from here.

    Thank you!
    -j
     
  9. JeffreyVentrella

    JeffreyVentrella

    Joined:
    Jun 16, 2016
    Posts:
    12
    Bunny,
    please see my response to spiney199.
    Thanks,
    -j
     
  10. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    That was probably just me skimming the long pre-amble then. Upon re-reading it still feels like you just wanted a simple all-in-one explanation.

    Considering multiple people seem to have made that interpretation... might be worth keeping it succinct in future.
     
  11. JeffreyVentrella

    JeffreyVentrella

    Joined:
    Jun 16, 2016
    Posts:
    12
    Thanks spiney!

    I assumed this was a discussion space. I was required to add some info before posting, and I chose "Discussion" thinking there would be people interested in talking about process in general - not just asking and answering tech questions (which is important and I guess the main purpose of this forum).

    Either way, your solution worked and I very much appreciate it.

    FYI - when I say "simple but complete" I guess what I mean is: simple = just the required code to achieve the stated goal (assuming I am able to state it clearly) and complete = you can drop it in a script and it works (which is 'sometimes' the case, which is awesome).

    Cheers!
    -j