I have a large list of names in JSON arranged by country and sorted into male and female. I would like to be able to access these names within unity to apply them to various generated game characters etc. When trying to do this I am receiving a Null reference error but am out of ideas on how to approach/ fix it.
I have tried creating a Dictionary to access the names. Here is an example of the JSON:
//json example
{
"India":{
"male":[
"A_Jay",
"Aaban",
"Aabid",
"Aabir",
"Aadam"
],
"female":[
"A_Jay",
"Aaban",
"Aabid",
"Aabir",
"Aadam"
]
},
"Usa":{
"male":[
"A_Jay",
"Aaban",
"Aabid",
"Aabir",
"Aadam"
],
"female":[
"A_Jay",
"Aaban",
"Aabid",
"Aabir",
"Aadam"
]
}
}
Here is my attempt at reading the json file:
//jsonreader.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstName {
public List<string> male;
public List<string> female;
}
public class FirstNames {
public Dictionary<string, FirstName> countries;
}
public class JSONReader : MonoBehaviour {
public TextAsset jsonFile;
void Start(){
FirstNames firstNamesInJson = JsonUtility.FromJson<FirstNames>(jsonFile.text);
Debug.Log("Found name: " + firstNamesInJson.countries["India"].male[0]);
}
}
My Debug Log is returning a Null reference error and I'm not sure why.
Your root json element is a json object which does not have countries property so you don't need the "root" object FirstNames, use Dictionary<string, FirstName> directly:
var firstNamesInJson = JsonUtility.FromJson<Dictionary<string, FirstName>>(jsonFile.text);
P.S.
thanks to #derHugo in the comments - Unity built-in JsonUtility does not support the Dictionary deserialization ATM (docs), so you will need to use 3rd party library for such dynamic deserialization with Dictionary (for example Newtonsoft.Json)
Unity built-in JsonUtility uses the normal Unity serialization rules .. Dictionary is not supported by it... the main answer is:
You will need a third-party JSON library in the first place!
Like e.g. Newtonsoft JSON.Net (it comes as a package) and then as was mentioned before your JSON doesn't have a root.
see Deserialize a Dictionary
void Start(){
var firstNamesInJson = JsonConvert.DeserializeObject<Dictionary<string, FirstName>>(jsonFile.text);
Debug.Log("Found name: " + firstNamesInJson.countries["India"].male[0]);
}
Related
This question already has answers here:
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Closed 1 year ago.
So I was trying to parse the JSON data into list/array, but kinda stuck at the format like this.
I'm relatively new at JSON, so I'm not very good at processing those stuffs
Here's my code:
void processJsonData(string _url)
{
jsonDataClass jsnData = JsonUtility.FromJson<jsonDataClass>(_url); //_url is based on the Json text below
Debug.Log(jsnData.data);
}
I uses this code to see if it managed to get the data from it, but
it shows the error like this
The JSON looks like this:
[{"country":"Malaysia","sales":9244},
{"country":"Singapore","sales":3103},
{"country":"Japan","sales":1755},
{"country":"China","sales":7835},
{"country":"United States","sales":2755},
{"country":"United Kingdom","sales":8611},
{"country":"Australia","sales":3877}]
My jsonDataClass looks like this:
using System.Collections;
using System.Collections.Generic;
using System;
[Serializable]
public class jsonDataClass
{
public List<basicData> data;
}
[Serializable]
public class basicData
{
public string country;
public float sales;
}
Updated answer:
Apparently in Unity you can not directly deserialize collection/list/array with the built-in JsonUtility (thanks to #derHugo for the info), if you looking for quick and probably dirty solution, use below code:
List<basicData> jsnData = JsonUtility.FromJson<List<basicData>>("{\"data\":" + _url + "}");
And if you like to learn more, see: Serialize and Deserialize Json and Json Array in Unity and JSON Serialization
I tried this piece of source code (based on NewtonSoft's JSON NuGet library), for reading a JSON file into a JSON object:
string str_File_Content = File.ReadAllText(openFileDialog1.FileName);
Rootobject existing_root = JsonConvert.DeserializeObject<Rootobject>(str_File_Content);
... and it almost worked: all JSON objects are loaded into the existing_root and in case of arrays, the number of objects seems to be correct.
But: the values of the attributes seem not to be filled in, as you can see here:
JSON file excerpt:
{
"project": {
"common.DESCRIPTION": "Some information",
existing_root excerpt in Watch window:
Expected :existing_root.project.commonDESCRIPTION : Some information
Real result :existing_root.project.commonDESCRIPTION : null
What can I do in order to make JsonConvert.DeserializeObject() not only handle the structure, but also the values?
Your json property name contains "." symbol, which is invalid for C# property name, so you can specify correct name to use during (de)serialization with JsonPropertyAttribute:
public class Project
{
[JsonProperty("common.DESCRIPTION")]
public string commonDESCRIPTION { get; set; }
}
I'm attempting to deserialize the pokemon API located at https://pokeapi.co/api/v2/pokemon/3 and I'm relatively new to working with json. I've made a class and method that loads whatever pokemon I choose into an object but can only get it to work with the simple keys like "name: value" and "id: value"
class Pokemon
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
// [JsonProperty("abilities")]
// public Dictionary<string, string>[] Abilities { get; set; }
//[JsonProperty("types")]
// public Dictionary<string, int>[] Types { get; set; }
//[JsonProperty("sprites")]
//public Dictionary<string, string> Sprites { get; set; }
public static Pokemon LoadPokemon(int num)
{
string json = new WebClient().DownloadString($"https://pokeapi.co/api/v2/pokemon/{num}");
Pokemon pokemon = JsonConvert.DeserializeObject<Pokemon>(json);
return pokemon;
}
}
All of the fields I can't make work I've commented out. Basically my question is how do I make those fields I've commented out actually load. I've tried a combination of dictionaries, arrays, and lists as datatypes but I can't seem to make anything work.
"sprites": {
"back_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/3.png",
"back_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/female/3.png",
"back_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/3.png",
"back_shiny_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/back/shiny/female/3.png",
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/3.png",
"front_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/female/3.png",
"front_shiny": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/3.png",
"front_shiny_female": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/shiny/female/3.png",
"other": {
"dream_world": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/dream-world/3.svg",
"front_female": null
},
"official-artwork": {
"front_default": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/3.png"
}
},
'Unexpected character encountered while parsing value: {. Path 'sprites.other', line 1, position 189387.' This is my usual error message when I attempt to load sprites, and the other attributes have similar errors. I can see why the "other:" key wouldn't work with Dict<string, string> but I also don't know how to avoid that problem since not every key in sprites has that format.
Also I'm using NewtonSoft.Json and System.Net packages. You can find the abbreviated version of the API at https://pokeapi.co/.
Trying to figure out the correct C# structure for very complex JSON documents can be quite difficult. I would suggest you start by using one of the many online JSON to C# convertor tools (e.g. https://app.quicktype.io/ or Google "JSON to C#") to take a sample JSON document and generate your class models. Using the sample JSON from https://pokeapi.co/, I used the quicktype tool to instantly generate the C# models. You can then take that C# code and modify it to suit your needs (create separate files for each class, remove unneeded properties, etc). But at least this provides you with a good (and valid) starting point. Tools like quicktype have config options that allow you to to refine the models, such as if you want JSON arrays to be C# arrays or Lists, etc.
"sprites" in your example will resolve to a Dictionary<string, object>. You will then, in your code, have to cast the value to either string, or another Dictionary<string, object> in the case of "dream_world" and "official_artwork".
I am able to read JSON in Unity when I specify field name by variables names in my class with JsonUtility. I want to make my script more generic so user can define which field name to use from multiple JSONs, which don't necessary use same field names.
// call this function to get array of JsonEntry objects
public static T[] FromJson<T>(string json)
{
return JsonUtility.FromJson<JsonEntries<T>>(json).Entries;
}
I am using this classes
[Serializable]
public class JsonEntries<T>
{
public T[] Entries;
}
[Serializable]
public class JsonEntry
{
// field names from one JSON example
public string USULAN_ID;
public string USULAN_LAT;
public string USULAN_LONG;
public string USULAN_URGENSI;
//TODO user defined filed names
}
Is it possible to let user define which field name to use, if I don't know the field names in advance with using JsonUtility in Unity?
Not using the JsonUtility, no!
With Unity's JsonUtility you have to know the field names beforehand and have classes implemented representing the structure of the JSON including the exact matching field names. And it is also limitted to certain types:
The API supports any MonoBehaviour-subclass, ScriptableObject-subclass, or plain class/struct with the [Serializable] attribute. The object you pass in is fed to the standard Unity serializer for processing, so the same rules and limitations apply as they do in the Inspector; only fields are serialized, and types like Dictionary<>; are not supported.
Passing other types directly to the API, for example primitive types or arrays, is not currently supported. For now you will need to wrap such types in a class or struct of some sort.
Assuming you need this only in one single level and a JSON looking like e.g.
{
"USULAN_ID":"some id",
"USULAN_LAT":"some lat",
"USULAN_LONG":"some long",
"USULAN_URGENSI":"some urgensi"
}
where the field names and count may change dynamically you could use SimpleJSON. In order to do so simply copy the content of SimpleJSON.cs into an accordingly named new file in your project and add using SimpleJSON; to a script where you want to use it.
And in the scene I would simply use a Dropdown to let the user decide which field to use
using SimpleJSON;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DropDownController : MonoBehaviour
{
[Header("Components References")]
public Dropdown dropdown;
[Header("Input")]
// Where ever you get the json data from
public string JsonString = "{\"USULAN_ID\":\"some id\", \"USULAN_LAT\":\"some lat\", \"USULAN_LONG\":\"some long\", \"USULAN_URGENSI\":\"some urgensi\"}";
[Header("Output")]
public string currentlySelectedKey;
public string currentlySelectedValue;
public Text selectedValueText;
private Dictionary<string, string> entries = new Dictionary<string, string>();
private void Awake()
{
// get the component
if (!dropdown) dropdown = GetComponent<Dropdown>();
// register a callback
dropdown.onValueChanged.AddListener(HandleValueChanged);
// where ever you need to call this method
UpdateOptions(JsonString);
}
public void UpdateOptions(string jsonString)
{
// parse the string to a JSONNode
var json = JSON.Parse(jsonString);
// unfortunately SimpleJson simply returns null
// and doesn't throw any exceptions so you have to do it on your own
if(json == null)
{
Debug.LogFormat(this, "Oh no! Something went wrong! Can't parse json: {0}", jsonString);
return;
}
// for the case this is called multiple times
// reset the lists and dictionaries
dropdown.options.Clear();
entries.Clear();
//optional add a default field
entries.Add("Please select a field", "no field selected!");
dropdown.options.Add(new Dropdown.OptionData("Please select a field"));
// JSONNode implements an Enumerator letting
// us simply iterate through all first level entries
foreach (var field in json)
{
// simply assume string keys and string values
// might brake if the provided json has another structure
// I'll skip the null checks here
var key = field.key;
var value = field.value;
entries.Add(key, value);
dropdown.options.Add(new Dropdown.OptionData(key));
}
// set default
dropdown.value = 0;
HandleValueChanged(0);
}
private void HandleValueChanged(int newValue)
{
currentlySelectedKey = dropdown.options[newValue].text;
currentlySelectedValue = entries[currentlySelectedKey];
// optional visual feedback for the user
selectedValueText.text = currentlySelectedValue;
// optional color feedback for not yet selected
selectedValueText.color = newValue == 0 ? Color.red : Color.black;
}
}
Note: Tested with Unity 2019.1; Dropdown might work different in 2019.2 since the UI was changed.
I have a slight situation. I'm interacting with a web service using RestSharp, where the service is requiring me to send the following as part of the request:
{
"a":"a value",
"b":"b value"
}
Which is all fine and dandy, because you could simply use a class such as this:
public class MyClass
{
public string A { get; set; }
public string B { get; set; }
}
However, I do not know know the property names at runtime. Therefore, I attempted to use an ExpandoObject, but of course, this simply serialized as a JSON array:
[
"a":"a value",
"b":"b value"
]
So, it would seem that I need to be able to serialize (and deserialize) a Dictionary (or IEnumerable<KeyValuePair<string, string>>) as a JSON object (in other words, use curly braces instead of a brackets).
Does anyone know how I might do this, preferably by using a Json.NET attribute, such that the functionality may be reused elsewhere?
how about using a JObject?
var obj = new JObject();
obj["One"] = "Value One";
obj["Two"] = "Value Two";
obj["Three"] = "Value Three";
var serialized = obj.ToString(Formatting.None);
gives you
{"One":"Value One","Two":"Value Two","Three":"Value Three"}
Use JavascripSerializer object from .net class libs. It supports reflection on the object it is serializing
see msdn docs