Search Unity

How to parse this JSON?

Discussion in 'Scripting' started by knightcube, May 9, 2020.

  1. knightcube

    knightcube

    Joined:
    Oct 16, 2018
    Posts:
    15
    This is the JSON - GitHub Gist

    Below is the code which is not working.

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.Networking;
    6.  
    7. public class MovieNetworking : MonoBehaviour
    8. {
    9.     string json;
    10.     void Start()
    11.     {
    12.         var url = "https://gist.githubusercontent.com/knightcube/580d0f94242cbd1fe349cded215dec00/raw/5ab4e39a148e16661d0938befac13ce3174f91cc/Movie.json";
    13.      
    14.         //StartCoroutine(GetRequest(Utils.TRENDING_URL));
    15.  
    16.         StartCoroutine(GetRequest(url));
    17.     }
    18.  
    19.     private class Movie
    20.     {
    21.         public int id;
    22.         public bool video;
    23.         public string title;
    24.         public int vote_count { get; set; }
    25.         public double vote_average { get; set; }
    26.         public string release_date { get; set; }
    27.         public string original_language { get; set; }
    28.         public string original_title { get; set; }
    29.         public IList<int> genre_ids { get; set; }
    30.         public string backdrop_path { get; set; }
    31.         public bool adult { get; set; }
    32.         public string overview { get; set; }
    33.         public string poster_path { get; set; }
    34.         public double popularity { get; set; }
    35.         public string media_type { get; set; }
    36.  
    37.         //public static Movie CreateFromJson(string jsonString)
    38.         //{
    39.         //    return JsonUtility.FromJson<Movie>(jsonString);
    40.         //}
    41.  
    42.     }
    43.  
    44.     private class Movies
    45.     {
    46.         public int page { get; set; }
    47.         public IList<Movie> results { get; set; }
    48.         public int total_pages { get; set; }
    49.         public int total_results { get; set; }
    50.  
    51.         public static Movies CreateFromJson(string jsonString)
    52.         {
    53.             return JsonUtility.FromJson<Movies>(jsonString);
    54.         }
    55.     }
    56.     private void MovieJsonParser(string json)
    57.     {
    58.         Debug.Log(json);
    59.  
    60.         Movies jsonObject = Movies.CreateFromJson(json);
    61.         Debug.Log(jsonObject.ToString());
    62.         int count = jsonObject.page;
    63.         Debug.Log(count);
    64.      
    65.     }
    66.  
    67.     IEnumerator GetRequest(string uri)
    68.     {
    69.         using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
    70.         {
    71.             // Request and wait for the desired page.
    72.             yield return webRequest.SendWebRequest();
    73.  
    74.             string[] pages = uri.Split('/');
    75.             int page = pages.Length - 1;
    76.  
    77.             if (webRequest.isNetworkError)
    78.             {
    79.                 Debug.Log(pages[page] + ": Error: " + webRequest.error);
    80.             }
    81.             else
    82.             {
    83.                 Debug.Log(pages[page] + ":\nReceived: ");
    84.             }
    85.             json = webRequest.downloadHandler.text;
    86.             MovieJsonParser(json);
    87.  
    88.         }
    89.     }
    90. }
    Where am I going wrong?
     
  2. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    You cannot parse JSON fields into C# properties, because they're technically methods.

    If you change your properties into fields, the JSON should parse correctly.
     
  3. knightcube

    knightcube

    Joined:
    Oct 16, 2018
    Posts:
    15
    I tried that but it didn't work. Can you try attaching the script to some GameObject in your scene and check?

    Do I need to have public setters and getters for all my private fields, even though I am accessing just one of those?
     
    Last edited: May 10, 2020
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    The class may have to be public, but the class certainly has to be decorated with this attribute:

    https://docs.microsoft.com/en-us/dotnet/api/system.serializableattribute?view=netcore-3.1

    Otherwise it will not ... serialize!

    Many other full JSON packages (such as JSON .NET, free on the asset store) will actually handle the properties just like the fields, but I Unity's "tiny JSON" does not. Here's LitJSON for comparison:

    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class garg : MonoBehaviour
    6. {
    7.     [System.Serializable]
    8.     public class Testing
    9.     {
    10.         public int AField;
    11.         public int AProperty { get; set; }
    12.     }
    13.  
    14.     void Start ()
    15.     {
    16.         var t = new Testing();
    17.  
    18.         t.AField = 123;
    19.         t.AProperty = 345;
    20.  
    21.         var s1 = JsonUtility.ToJson(t);
    22.         Debug.Log( "Unity JSON:" + s1);
    23.  
    24.         var s2 = LitJson.Json.serialize(t);
    25.         Debug.Log( "LitJSON:" + s2);
    26.     }
    27. }
    And the output:

    Screen Shot 2020-05-09 at 6.45.01 PM.png

    Addendum: the class CAN be private and it will serialize the same way as above.
     
  5. knightcube

    knightcube

    Joined:
    Oct 16, 2018
    Posts:
    15
    I made the class public, added the Serializable attribute, used the JSON .Net Unity asset, converted the properties to fields and it worked. Here is the updated code -

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using Newtonsoft.Json;
    5. using UnityEngine;
    6. using UnityEngine.Networking;
    7.  
    8. public class MovieNetworking : MonoBehaviour
    9. {
    10.     string json;
    11.     void Start()
    12.     {
    13.         var url = "https://gist.githubusercontent.com/knightcube/580d0f94242cbd1fe349cded215dec00/raw/848fa4a01f2363cd82310738f26a633e4ea0285b/Movie.json";
    14.  
    15.        // StartCoroutine(GetRequest(Utils.TRENDING_URL));
    16.  
    17.         StartCoroutine(GetRequest(url));
    18.     }
    19.  
    20.     [Serializable]
    21.     public class Movie
    22.     {
    23.         public int id;
    24.         public bool video;
    25.         public string title;
    26.         public int vote_count;
    27.         public double vote_average;
    28.         public string release_date;
    29.         public string original_language;
    30.         public string original_title;
    31.         public int[] genre_ids;
    32.         public string backdrop_path;
    33.         public bool adult;
    34.         public string overview;
    35.         public string poster_path;
    36.         public double popularity;
    37.         public string media_type;
    38.  
    39.         //public static Movie CreateFromJson(string jsonString)
    40.         //{
    41.         //    return JsonUtility.FromJson<Movie>(jsonString);
    42.         //}
    43.  
    44.     }
    45.  
    46.     [Serializable]
    47.     public class Movies
    48.     {
    49.         public int page;
    50.         public Movie[] results;
    51.         public int total_pages;
    52.         public int total_results;
    53.  
    54.         public Movies() {
    55.         }
    56.  
    57.         public static Movies CreateFromJson(string jsonString)
    58.         {
    59.             return JsonConvert.DeserializeObject<Movies>(jsonString);
    60.             //return JsonUtility.FromJson<Movies>(jsonString);
    61.         }
    62.  
    63.         public Movie[] GetMovies(){
    64.             return results;
    65.         }
    66.     }
    67.  
    68.     private void MovieJsonParser(string json)
    69.     {
    70.         Debug.Log(json);
    71.  
    72.         Movies jsonObject = Movies.CreateFromJson(json);
    73.         Debug.Log(jsonObject.ToString());
    74.         int count = jsonObject.GetMovies().Length;
    75.  
    76.         foreach(Movie movie in jsonObject.GetMovies())
    77.         {
    78.             Debug.Log(movie.title);
    79.         }
    80.      
    81.     }
    82.  
    83.     IEnumerator GetRequest(string uri)
    84.     {
    85.         using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
    86.         {
    87.             // Request and wait for the desired page.
    88.             yield return webRequest.SendWebRequest();
    89.  
    90.             string[] pages = uri.Split('/');
    91.             int page = pages.Length - 1;
    92.  
    93.             if (webRequest.isNetworkError)
    94.             {
    95.                 Debug.Log(pages[page] + ": Error: " + webRequest.error);
    96.             }
    97.             else
    98.             {
    99.                 Debug.Log(pages[page] + ":\nReceived: ");
    100.             }
    101.             json = webRequest.downloadHandler.text;
    102.             MovieJsonParser(json);
    103.  
    104.         }
    105.     }
    106. }
    I think making those fields public is not a good practice. Is it? If not then how can I keep those encapsulated?
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,735
    Spank any naughty programmers that touch them?
     
    Munchy2007 likes this.
  7. Vryken

    Vryken

    Joined:
    Jan 23, 2018
    Posts:
    2,106
    You can make the fields private and add the [SerializeField] attribute onto each one:
    Code (CSharp):
    1.     [Serializable]
    2.     public class Movie
    3.     {
    4.         [SerializeField] private int id;
    5.         [SerializeField] private bool video;
    6.         [SerializeField] private string title;
    7.         [SerializeField] private int vote_count;
    8.         [SerializeField] private double vote_average;
    9.         [SerializeField] private string release_date;
    10.         [SerializeField] private string original_language;
    11.         [SerializeField] private string original_title;
    12.         [SerializeField] private int[] genre_ids;
    13.         [SerializeField] private string backdrop_path;
    14.         [SerializeField] private bool adult;
    15.         [SerializeField] private string overview;
    16.         [SerializeField] private string poster_path;
    17.         [SerializeField] private double popularity;
    18.         [SerializeField] private string media_type;
    19.  
    20.         //public static Movie CreateFromJson(string jsonString)
    21.         //{
    22.         //    return JsonUtility.FromJson<Movie>(jsonString);
    23.         //}
    24.  
    25.     }
    Code (CSharp):
    1.     [Serializable]
    2.     public class Movies
    3.     {
    4.         [SerializeField] private int page;
    5.         [SerializeField] private Movie[] results;
    6.         [SerializeField] private int total_pages;
    7.         [SerializeField] private int total_results;
    8.  
    9.         public Movies() {
    10.         }
    11.  
    12.         public static Movies CreateFromJson(string jsonString)
    13.         {
    14.             return JsonConvert.DeserializeObject<Movies>(jsonString);
    15.             //return JsonUtility.FromJson<Movies>(jsonString);
    16.         }
    17.  
    18.         public Movie[] GetMovies(){
    19.             return results;
    20.         }
    21.     }
     
  8. knightcube

    knightcube

    Joined:
    Oct 16, 2018
    Posts:
    15
    LOL
     
  9. knightcube

    knightcube

    Joined:
    Oct 16, 2018
    Posts:
    15
    Thanks. This worked :)
     
    Kurt-Dekker likes this.