Search code examples
jsonunity-game-enginejsonserializer

Unity JSON Serializer not allowing for calling JSON Fields n JSON Objects


The have an issue where I am unable to call nested JSON Objects from a scraped website. The scraping process works prefectly, but the JSON Serializing part is the only issue. My code is shown below:

private void GetHtmlAsync()
    {
        var url = "https://opentdb.com/api.php?amount=10";

        var httpClient = new HttpClient();
        var html = httpClient.GetStringAsync(url);

        DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(MyDetail));
        MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(html.Result));
        stream.Position = 0;
        MyDetail dataContractDetail = (MyDetail) jsonSerializer.ReadObject(stream);
        text.text = "" + dataContractDetail.results[1];
        //text.text = string.Concat("Test: ", dataContractDetail.question, " " + dataContractDetail.correct_answer);
    }

    public class MyDetail
    {
        [DataMember]
        public Dictionary<string, questions> results
        {
            get;
            set;
        }

        public class questions
        {
            public string question { get; set; }
            public string correct_answer { get; set; }

        }

        [DataMember]
        public string response_code
        {
            get;
            set;
        }
    }

This code is the code that does not work, in that I try to call the first object in results by doing "results[1]", which returns an error after I attach, say, "question" to it by doing "results[1].question". This syntax seems reasonable, so I don;t understand why it is not working. My JSON File is shown below:

{
"response_code": 0,
"results": [
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "What is the name of the virus in &quot;Metal Gear Solid 1&quot;?",
"correct_answer": "FOXDIE",
"incorrect_answers": [
"FOXENGINE",
"FOXALIVE",
"FOXKILL"
]
},
{
"category": "Geography",
"type": "multiple",
"difficulty": "easy",
"question": "What is the official language of Costa Rica?",
"correct_answer": "Spanish",
"incorrect_answers": [
"English",
"Portuguese",
"Creole"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "In Fallout 4, which type of power armor is first encountered in the early mission &quot;When Freedom Calls&quot; in a crashed Vertibird?",
"correct_answer": "T-45",
"incorrect_answers": [
"T-51",
"T-60",
"X-01"
]
},
{
"category": "Politics",
"type": "boolean",
"difficulty": "medium",
"question": "George W. Bush lost the popular vote in the 2004 United States presidential election.",
"correct_answer": "False",
"incorrect_answers": [
"True"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "medium",
"question": "In &quot;Halo 2&quot;, what is the name of the monitor of Installation 05?",
"correct_answer": "2401 Penitent Tangent",
"incorrect_answers": [
"343 Guilty Spark",
"031 Exuberant Witness",
"252 Biodis Expolsion"
]
},
{
"category": "Entertainment: Books",
"type": "multiple",
"difficulty": "medium",
"question": "The book &quot;Fahrenheit 451&quot; was written by whom?",
"correct_answer": "Ray Bradbury",
"incorrect_answers": [
"R. L. Stine",
"Wolfgang Amadeus Mozart",
"Stephen King"
]
},
{
"category": "Entertainment: Cartoon & Animations",
"type": "multiple",
"difficulty": "hard",
"question": "In &quot;Rick and Morty&quot;, from which dimension do Rick and Morty originate from?",
"correct_answer": "C-137",
"incorrect_answers": [
"J1977",
"C-136",
"C500-a"
]
},
{
"category": "Entertainment: Video Games",
"type": "multiple",
"difficulty": "hard",
"question": "In which game did the character &quot;Mario&quot; make his first appearance?",
"correct_answer": "Donkey Kong",
"incorrect_answers": [
"Super Mario Bros.",
"Super Mario Land",
"Mario Bros."
]
},
{
"category": "Entertainment: Film",
"type": "multiple",
"difficulty": "hard",
"question": "What was Humphrey Bogart&#039;s middle name?",
"correct_answer": "DeForest",
"incorrect_answers": [
"DeWinter",
"Steven",
"Bryce"
]
},
{
"category": "Entertainment: Cartoon & Animations",
"type": "boolean",
"difficulty": "medium",
"question": "In &quot;Avatar: The Last Airbender&quot; and &quot;The Legend of Korra&quot;, Lavabending is a specialized bending technique of Firebending.",
"correct_answer": "False",
"incorrect_answers": [
"True"
]
}
]
}

Solution

  • There are many issues in your code. I don't know all the libraries you are using but here is how I would do it.

    First of all you start a GetStringAsync but you continue immediately without waiting for results. I don't know all the libraries you are using ofcourse maybe it is supposed to be like that?

    However I would rather use Unity's UnityWebRequest.Get

    private void GetHtmlAsync()
    {
        StartCoroutine(DownloadJson());
    }
    
    
    private IEnumerator DownloadJson()
    {
        var url = "https://opentdb.com/api.php?amount=10";
        using(var uwr = UnityWebRequest.Get(url))
        {
            // send the request and wait for result
            yield return uwr.SendWebRequest();
            // Check for success!
            if(uwr.isNetworkError || uwr.isHttpError || !string.IsNullOrWhiteSpace(uwr.error))
            {
                Debug.LogError($"Download failed with {uwr.responseCode} reason: {uwr.error}", this);
                yield break;
            }
    
            var json = uwr.DownloadHandler.text;
    
            // ... se below
        }
    }
    

    Again I don't know your JSON library but your class seems not to match the JSON data structure which would be (by simply jamming it through json2csharp)

    [Serializable]
    public class Result
    {
        public string category;
        public string type;
        public string difficulty;
        public string question;
        public string correct_answer;
        public List<string> incorrect_answers;
    }
    
    [Serializable]
    public class MyDetail
    {
        public int response_code;
        public List<Result> results;
    }
    

    for Unity I would use [Serializable] and also remove all the {get;set} in order to not use properties but fields.

    Then you could simply use Unity's JsonUtility

    ...
    MyDetail dataContractDetail = JsonUtility.FromJson<MyDetail>(json);
    

    Then as mentioned in the comments note that array indices in c# are 0-based so the first element would be

    var firstResult = dataContractDetail.results[0];
    

    Now the question is what do you want to see on your text? The firstResult is no string but rather a class having various members! You could e.g. want to display the question like

    text.text = firstResult.question;