2
\$\begingroup\$

I'm trying to use Json to hold game dialog in a text asset file. I've tried to make a barebones project to test this and it is failing with the Error Message: ArgumentException: JSON must represent an object type.

I have a class called LineData which has a few properties each line of dialog contains. In the Json there are just two lines for now. When I import and parse it to try and make a reference to one of the lines of dialog it fails.

Here is all the code:

dialogJson.JSON:

[
{
    "lineID": "BEDROOM_DAVE_0001",
    "lineDialog": "Yaawwn, up i get for another fun packed day.",
    "lineDuration": 3
},
{
    "lineID": "BEDROOM_DAVE_0002",
    "lineDialog": "Well I think I need a cigarette before I get to work!",
    "lineDuration": 3
}
]

LineData.cs

using System;

[Serializable]
public class LineData
{
    public string lineID;
    public string lineDialog;
    public float lineDuration;
}

GameController.cs

using UnityEngine;

public class GameController : MonoBehaviour
{
    TextAsset Dialog_Data;

    private void Start()
    {
        Dialog_Data = Resources.Load<TextAsset>("txt/dialogJson");

        LineData line1 = JsonUtility.FromJson<LineData>(Dialog_Data.text);
        Debug.Log("line 1 dialog = " + line1.lineDialog);
    }
}

NOTE: I read here (https://answers.unity.com/questions/1503047/json-must-represent-an-object-type.html) that Unity "can't parse if the json consists of an array of objects". But I don't understand his explanation on how to get around this. Thanks

EDIT: I now have it without error, but the debug message is just empty didnt seem to get any data) . Here is the json now after the edits:

{ "LineData":
[
{
    "lineID": "BEDROOM_DAVE_0001",
    "lineDialog": "Yaawwn, up i get for another fun packed day.",
    "lineDuration": 3
},
{
    "lineID": "BEDROOM_DAVE_0002",
    "lineDialog": "Well I think I need a cigarette before I get to work!",
    "lineDuration": 3
}
]
}

I've also altered the GameController.cs like this (but now it says Index is outside array bounds:

public class GameController : MonoBehaviour
{
TextAsset Dialog_Data;

private void Start()
{
    LineData[] lines = new LineData[2];
    Dialog_Data = Resources.Load<TextAsset>("txt/dialogJson");
    lines = JsonUtility.FromJson<LineData[]>(Dialog_Data.text);
    Debug.Log("line 1 dialog = " + lines[0].lineDialog);
}
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ One small note: I see you trying to "prepare" an array with two slots with lines = new LineData[2];, then load data into those slots with lines = JsonUtility.... C# assignment does not work that way. It doesn't "write into" an existing array - it gives you a whole new array. So the first empty array you created is thrown out as garbage, and you might as well have never created it at all. You can write an individual index into an array with lines[0] = whatever, but when you assign to lines itself with no brackets you're always just replacing the old reference with a new one. \$\endgroup\$
    – DMGregory
    Dec 17, 2018 at 20:19
  • \$\begingroup\$ nice i do understand that actually, i was kind of clutching at straws when i tried writing it like that. But its always helpful to have it clearly pointed out by a professional looking at my code specifically. Cheers pal \$\endgroup\$ Dec 17, 2018 at 20:25

1 Answer 1

2
\$\begingroup\$

When you write a line like this:

LineData line1 = JsonUtility.FromJson<LineData>(Dialog_Data.text);

You're saying "the JSON in this text string represents exactly one instance of the type LineData". ie. the program is expecting something like this:

{
    "lineID": "BEDROOM_DAVE_0001",
    "lineDialog": "Yaawwn, up i get for another fun packed day.",
    "lineDuration": 3
}

That's everything it needs to be able to populate exactly one LineData object.

When instead you give it JSON like this:

[
    {
        "lineID": "BEDROOM_DAVE_0001",
        "lineDialog": "Yaawwn, up i get for another fun packed day.",
        "lineDuration": 3
    },
    {
        "lineID": "BEDROOM_DAVE_0002",
        "lineDialog": "Well I think I need a cigarette before I get to work!",
        "lineDuration": 3
    }
]

Then you're breaking your promise to the program that you were going to give it exactly one instance of type LineData. Instead you've given it two instances packed into an array of type LineData[]. In the eyes of a CPU, this is as different as being handed a chocolate bar to eat versus having a shipping container full of chocolate bars dropped on your foot. ;)

So, we need to tell the program to expect a shipping container. First, by defining what a shipping container full of LineData looks like:

[Serializable]
public class LineDataCollection {
    public LineData[] lines;
}

Then formatting our JSON to match this definition (one member named "lines" that holds an array of LineData objects):

{
    "lines": [
        { /* Your first line data goes here */ },
        { /* Your second line data goes here... */ } 
     ]
}

Then we need to tell our code to unpack a shipping container worth from the text, instead of just one LineData:

LineDataCollection allLines = JsonUtility.FromJson<LineDataCollection>(Dialog_Data.text);
// Select the first line from the collection, and print its dialogue:
Debug.Log("line 1 dialog = " + allLines.lines[0].lineDialog);
\$\endgroup\$
1
  • 3
    \$\begingroup\$ AAAHHHH!!! I hear the sound of pennies dropping my friend!!!! Thank you so much !! In all the documentation and tutorials i had read they never seemed to mention making two classes. But now I fully understand it, hopefully I can apply this to making a nice semi-complex game scene. You rock! \$\endgroup\$ Dec 17, 2018 at 20:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .