1

I'm trying to read this JSON file. If it looks messy, here is a Pastebin of the JSON. If the code looks messy, here is the Pastebin for that as well! (Ignore the return statement in the DeserializeQuestions() function). This works flawlessly when I test it in the editor, but it breaks when I test it on an Android device.

I get this error message that is as cryptic as the Voynich Manuscript:

enter image description here

{
  "Questions": {
    "0": [ "Are all of these questions temporary?", "Yes they are", "No", "No nr 2", "No nr 3" ],
    "1": [ "Roughly how tall is Mount Everest?", "8848 meters", "9343 meters", "8.322 km", "73000 mm" ],
    "2": [ "What are the worlds most populated countries?", "China, India and the US", "China, Russia and the US", "Russia, Brazil and the Soviet Union", "England, Russia and the US" ],
    "3": [ "What is the highest mountain top in Stord?", "Mehammarsåta", "Mennene", "Utslettefjellet", "Siggjo" ],
    "4": [ "What field did Stephen Hawking mainly study?", "Theoretical physics", "Relative physics", "Imaginary physics", "The gravity of your mom" ],
    "5": [ "What field did Stephen Hawking mainly study?", "Theoretical physics", "Relative physics", "Imaginary physics", "The gravity of your mom" ],
    "6": [ "What is Asgeir Asgeirson's favorite car manufacturar?", "Mercedes", "Ferrari", "Lamborghini", "
}

SPACE

using UnityEngine;
using System.IO;
using System.Collections.Generic;
using System;
using Newtonsoft.Json.Linq;

public class JsonFileWriter : MonoBehaviour
{
    public GameController gameController;
    public Settings settings;
    public List<Dictionary<string, string>> allQuestions = new List<Dictionary<string, string>>();
    public string path;
    public string pathGameData;

    public void Start()
    {

        path = "jar:file://" + Application.dataPath + "!/assets/data.json";
#if UNITY_EDITOR
        path = Path.Combine(Application.streamingAssetsPath, "data.json");
#endif
        pathGameData = Path.Combine(Application.persistentDataPath, "gameData.json");
        //List<JsonString> temp = new List<JsonString>();
        //temp.Add(new JsonString(new string[]{ "The question","The correct answer","Answer 1","Answer 2","Answer 3"}));
        //SerializeData();
        DeserializeQuestions();
    }

    public void SerializeGameData(GameData data)
    {
        System.DateTime epochStart = new System.DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc);
        string cur_time = (int)(System.DateTime.UtcNow - epochStart).TotalSeconds + "";
        data.date = cur_time;
        if (!File.Exists(pathGameData))
        {
            File.WriteAllText(pathGameData, "");
        }
        string containerJson = File.ReadAllText(pathGameData);
        GameDataContainer container = new GameDataContainer();
        container.container = new List<GameData>();
        if(containerJson != "")
        {
            container = JsonUtility.FromJson<GameDataContainer>(containerJson);
        }
        container.container.Add(data);
        if(container.container.Count > settings.save_this_many_games)
        {
            int lowest = (int)(System.DateTime.UtcNow - epochStart).TotalSeconds;
            foreach(GameData s in container.container)
            {
                if(Int32.Parse(s.date) < lowest)
                {
                    lowest = Int32.Parse(s.date);
                }
            }
            for (int i = container.container.Count - 1; i >= 0; i--)
            {
                if(Int32.Parse(container.container[i].date) == lowest)
                {
                    container.container.Remove(container.container[i]);
                }
            }
        }
        containerJson = JsonUtility.ToJson(container);
        File.WriteAllText(pathGameData, containerJson);
    }

    public void DeserializeQuestions()
    {
        string json;

        if (Application.platform == RuntimePlatform.Android)
        {
            WWW reader = new WWW(path);
            while (!reader.isDone) { }

            json = reader.text;
        }

        json = File.ReadAllText(path);


        JObject jo = JObject.Parse(json);
        Dictionary<string, List<string>> values = jo.SelectToken("Questions", false).ToObject<Dictionary<string, List<string>>>();
        foreach(var kv in values)
        {
            Dictionary<string, string> temp = new Dictionary<string, string>();
            int i = 0;
            foreach(string s in kv.Value)
            {
                temp.Add(i + "", s);
                i++;
            }
            allQuestions.Add(temp);
        }
    }
}

[Serializable]
public class GameData
{
    public string date;
    public string questions;
    public List<string> playerScoresNames;
    public List<int> playerScoresScores;
    public List<string> playerStatesNames;
    public List<int> playerStatesStates;
}

[Serializable]
public class GameDataContainer
{
    public List<GameData> container;
}

SPACE

03-10 20:18:11.653: E/Unity(28401): JsonReaderException: Unexpected character encountered while parsing value: ?. Path '', line 0, position 0.
03-10 20:18:11.653: E/Unity(28401):   at Newtonsoft.Json.JsonTextReader.ParseValue () [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):   at Newtonsoft.Json.JsonTextReader.Read () [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):   at Newtonsoft.Json.Linq.JObject.Load (Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Linq.JsonLoadSettings settings) [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):   at Newtonsoft.Json.Linq.JObject.Parse (System.String json, Newtonsoft.Json.Linq.JsonLoadSettings settings) [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):   at Newtonsoft.Json.Linq.JObject.Parse (System.String json) [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):   at JsonFileWriter.DeserializeQuestions () [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):   at JsonFileWriter.Start () [0x00000] in <filename unknown>:0 
03-10 20:18:11.653: E/Unity(28401):  
03-10 20:18:11.653: E/Unity(28401): (Filename:  Line: -1)
Nitin Bisht
  • 5,053
  • 4
  • 14
  • 26
Asgeir
  • 593
  • 6
  • 21
  • Just out of curiosity, why are you mixing `JsonUtility.FromJson` and `JObject.Parse`? Why not make a data model for the questions and use `JsonUtility.FromJson` for that also? Does it not handle dictionaries? – dbc Mar 10 '19 at 18:55
  • Correct! JsonUtility doesn't handle dictionaries as far as I know! – Asgeir Mar 10 '19 at 18:59
  • Possibly your file has a [BOM](https://en.wikipedia.org/wiki/Byte_order_mark) at the beginning. `File.ReadAllText()` should process and remove that, but maybe it's not working correctly on unity3d/Android. So you could try the workaround from [this answer](https://stackoverflow.com/a/1319226) to [Strip Byte Order Mark from string in C#](https://stackoverflow.com/q/1317700). Printing the numeric values of the first 100 or so bytes of the `json` string would make that clear. – dbc Mar 10 '19 at 19:02
  • Printing the numeric values of the first 100 or so bytes of the `json` string would make that clear. Related: [Why is File.ReadAllBytes result different than when using File.ReadAllText?](https://stackoverflow.com/q/26101859). – dbc Mar 10 '19 at 19:05
  • Oh OK in your most recent edit it's clear you're using something called `WWW` to read your text on Android, not `File.ReadAllText()`. I don't know this class at all, but regardless, it's now much more plausible that a BOM has been left at the beginning of the string. – dbc Mar 10 '19 at 19:16
  • @DBC I tried your solution, but it unfortunately did not work! I might have understood the solution wrong though. [Here is my implementation of the solution](https://pastebin.com/pdg6TW1q). I have also added the error message in plain text if that helps anybody! – Asgeir Mar 10 '19 at 19:22
  • The [suggested solution](https://stackoverflow.com/a/1319226/3744182) would be `if (json.StartsWith(_byteOrderMarkUtf8, StringComparison.Ordinal)) { json = json.Remove(0, _byteOrderMarkUtf8.Length); }` – dbc Mar 10 '19 at 19:24
  • @DBC I'm still getting the same error unfortunately. I think I might have to find a new JSON reader to use for Android. – Asgeir Mar 10 '19 at 19:35
  • Did you try printing the numeric values of the first 100 or so bytes of the `json` string? – dbc Mar 10 '19 at 19:37
  • @DBC I tried printing the JSON string itself, and it looks perfectly fine. I printed the [bytes and it looks like this](https://pastebin.com/cVzyPxf0). I'm not sure it's what you meant though. – Asgeir Mar 10 '19 at 19:56
  • Well there's a question mark character at the beginning of your string: `?{ "Questions": {` See https://dotnetfiddle.net/DzsL2R for a dump of your pastebin. You need to strip that off before you can parse the JSON. (The `?` character isn't a BOM since it's a printable character. I don't know why it might be there.) – dbc Mar 10 '19 at 23:34
  • @DBC You are a god in my eyes. How the hell the question mark appears, I have no idea, but removing it before parsing works! I'm not sure how I mark your comment as the correct answer though! Thanks a lot! – Asgeir Mar 11 '19 at 15:41
  • Possible duplicate of [Strip Byte Order Mark from string in C#](https://stackoverflow.com/questions/1317700/strip-byte-order-mark-from-string-in-c-sharp) – derHugo Mar 11 '19 at 17:38
  • @derHugo It's kind of the same thing, except I had a mystery question mark appear in my json string when reading using WWW. It's was solvable by just deleting the question mark. I will add an answer to this question now, as I forgot to do it earlier. – Asgeir Mar 11 '19 at 19:36
  • The "question mark" **is** the BOM that can no be represented as a correct symbol so a `?` is displayed instead. But it is not the same as a `?` character. – derHugo Mar 11 '19 at 20:44

1 Answers1

0

I do not know why, but when reading my JSON file using WWW, a mystery question mark was placed at the beginning of the string. This threw the JSON parser off. If you just remove the question mark, it works fine. I can guarantee that this is not the optimal way of doing it, but it works. What I'm doing here is just removing the first ASCII encoded byte.

public void DeserializeQuestions()
{
    if (Application.platform == RuntimePlatform.Android)
    {
        WWW reader = new WWW(path);
        while (!reader.isDone) { }

        json = reader.text;

        byte[] bytes = Encoding.ASCII.GetBytes(json);
        byte[] tempBytes = new byte[bytes.Length-1];
        for(var i = 1; i < bytes.Length; i++)
        {
            tempBytes[i - 1] = bytes[i];
        }
        string tempString = System.Text.Encoding.ASCII.GetString(tempBytes);
        json = tempString;
    }
    else
    {
        json = File.ReadAllText(path);
    }

    JObject jo = JObject.Parse(json);
    Dictionary<string, List<string>> values = jo.SelectToken("Questions", false).ToObject<Dictionary<string, List<string>>>();
    foreach(var kv in values)
    {
        Dictionary<string, string> temp = new Dictionary<string, string>();
        int i = 0;
        foreach(string s in kv.Value)
        {
            temp.Add(i + "", s);
            i++;
        }
        allQuestions.Add(temp);
    }
}
Asgeir
  • 593
  • 6
  • 21