Cannot deserialize data with JsonUtility [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Closed 3 years ago.
I'm new to using Json and I cant figure out why data isn't deserializing. I tried many things and I don't know what the problem is.
First I need to get data from this link https://jsonplaceholder.typicode.com/users
and then i need to deserialize it. (Need to use REST request)
Whenever I wanna use any property it tells me its NULL "NullReferenceException: Object reference not set to an instance of an object"
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Net;
using System.IO;
public class Houses : MonoBehaviour
{
public GameObject Prefab;
void Update()
{
if (Input.GetKey(KeyCode.T))
{
onClick();
}
}
public void onClick()
{
UnityWebRequest request = UnityWebRequest.Get("https://jsonplaceholder.typicode.com/users");
RootObject[] rootObjects = JsonUtility.FromJson<RootObject[]>(request.downloadHandler.text);
//Debug.Log(request.downloadHandler.text);
//foreach (var item in rootObjects)
//{
// Debug.Log(item.id);
//}
//----------------------------------------------------------------------------------------
//Latitude is the Y axis, longitude is the X axis
//for (int i = 0; i < rootObjects.Length; i++)
//{
// float x = float.Parse(rootObjects[i].address.geo.lng);
// float y = float.Parse(rootObjects[i].address.geo.lat);
// Instantiate(Prefab, new Vector3(x, y, 0f), Quaternion.identity);
//}
}
}
//public class ListItems
//{
// //public RootObject[] rootObjects;
//public List<RootObject> rootObjects;
//}
[System.Serializable]
public class Geo
{
public string lat { get; set; }
public string lng { get; set; }
}
[System.Serializable]
public class Address
{
public string street { get; set; }
public string suite { get; set; }
public string city { get; set; }
public string zipcode { get; set; }
public Geo geo { get; set; }
}
[System.Serializable]
public class Company
{
public string name { get; set; }
public string catchPhrase { get; set; }
public string bs { get; set; }
}
[System.Serializable]
public class RootObject
{
public int id; //{ get; set; }
public string name { get; set; }
public string username { get; set; }
public string email { get; set; }
public Address address { get; set; }
public string phone { get; set; }
public string website { get; set; }
public Company company { get; set; }
}
I would be really grateful if someone could help me out.
EDIT:
So i made some changes. Turns out thanks to you guys i didnt give the UnityWebRequest time to get the data.
Now im getting a different error "ArgumentException: JSON must represent an object type."
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Net;
using System.IO;
public class Houses : MonoBehaviour
{
public GameObject Prefab;
public string jsonURL;
void Update()
{
if (Input.GetKeyUp(KeyCode.T))
{
onClick();
}
}
public void processJsonData(string _url)
{
RootObject[] rootObjects = JsonUtility.FromJson<RootObject[]>(_url);
Debug.Log(rootObjects[0].id);
}
IEnumerator getData()
{
Debug.Log("Procesing data, please wait.");
//WWW _www = new WWW(jsonURL);
UnityWebRequest request = UnityWebRequest.Get("https://jsonplaceholder.typicode.com/users");
yield return request.SendWebRequest();
if (request.error == null)
{
processJsonData(request.downloadHandler.text);
}
else
{
Debug.Log("Oops something went wrong.");
}
}
public void onClick()
{
//----------------------------------------------------------------------------------------
//Latitude is the Y axis, longitude is the X axis
//for (int i = 0; i < rootObjects.Length; i++)
//{
// float x = float.Parse(rootObjects[i].address.geo.lng);
// float y = float.Parse(rootObjects[i].address.geo.lat);
// Instantiate(Prefab, new Vector3(x, y, 0f), Quaternion.identity);
//}
StartCoroutine(getData());
}
}
//public class ListItems
//{
// //public RootObject[] rootObjects;
//public List<RootObject> rootObjects;
//}
[System.Serializable]
public class Geo
{
public string lat;
public string lng;
}
[System.Serializable]
public class Address
{
public string street;
public string suite;
public string city;
public string zipcode;
public Geo geo;
}
[System.Serializable]
public class Company
{
public string name;
public string catchPhrase;
public string bs;
}
[System.Serializable]
public class RootObject
{
public int id;
public string name;
public string username;
public string email;
public Address address;
public string phone;
public string website;
public Company company;
}```

Untiy's JsonUtility is a relatively performant but also extremely limited serializer. It only supports serializing fields, while the objects you are trying to deserialize are entirely made up of properties, which it does not support.
(By the way, that Unity's default serializer doesn't support properties, is also why they don't show up in the editor while fields do.)
Since it doesn't know how to set the property, it doesn't, and the property remains with its default valuel. In the case of refrence types (string, Address, etc.) the default value is NULL, which makes trying to access them throw an exception.
All you have to do to fix this in your case is change the classes you want to deserialize so that this:
public class Geo
{
public string lat { get; set; }
public string lng { get; set; }
}
Becomes this:
public class Geo
{
public string lat;
public string lng;
}
Do that for all your classes, and you should be golden.
EDIT: As Philip B. Mentioned in his answer, you are also not using UnityWebRequest.Get correctly.
UnityWebRequest doesnt resolve immediatly, and you must either yield return... to it from inside a Unity Coroutine, or wait for it to resolve with a blocking loop like so:
UnityWebRequest request = UnityWebRequest.Get("https://jsonplaceholder.typicode.com/users");
request.SendWebRequest();
while (!request.isDone) {}
Of course this is considered very bad form, as it freezes the game untill the data is downloaded. However, under the hood Unity handles the downloads on background threads, so you won't be blocking the download itself while you wait.
You should also query the request to see if any errors came up so you can handle them if you want to, as you might also be having issues with the download itself.
if (request.isHttpError || request.isNetworkError)
{
//... handle errors here
}
else
{
RootObject[] rootObjects = JsonUtility.FromJson<RootObject[]>(request.downloadHandler.text);
}
However, I'd really recommend you use coroutines here as described in Unity's UnityWebRequest.Get documentation. If you're new to Unity Coroutines please read up, it's a valuable tool for Unity development.
EDIT 2: Per this answer JsonUtility doesn't support serializing an array as a top level object, so you'll have to wrap your array in an object first, and deserialize that. You'll also have to make the top level object in the json file an object that holds that array.
[Serializable]
public class Routes
{
public RootObject[] roots;
}
If you don't control the contents of the file, (and again this idea is coming from that excelent answer I linked to) you can do so with some string magic.
string jsonString = $#"{{""roots"":{request.downloadHandler.text}}}";

You are trying to access request.downloader.text which is NULL (hence the "NullReferenceException: Object reference not set to an instance of an object") since you didn't send your GET request.
If you follow this link on how to use UnityWebRequest.Get you will see that you need more than just UnityWebRequest.Get("Your_Url"); to get your JSON string from the URL.
Try the following :
public void onClick()
{
using (UnityWebRequest request = UnityWebRequest.Get("https://jsonplaceholder.typicode.com/users"))
{
request.SendWebRequest();
RootObject[] rootObjects = JsonUtility.FromJson<RootObject[]>(request.downloadHandler.text);
}
}
Hope this will help you.

Related

Get value from Json url with a changing variable

I want to get the price of any crypto coin from BitZ api.
I have the code like this:
string coinName;
string jsonURL = "https://apiv2.bitz.com/Market/coinRate?coins=" + coinName;
I will give the variable coinName the value I want for example coinName = "btc" and I want the price in USDT
The problem here is the Json structure it contains the coin name I will end up with tons of code lines if do this for every coin,
public class Btc
{
public string usdt { get; set; }
}
public class Data
{
public Btc btc { get; set; }
}
public class Root
{
public int status { get; set; }
public string msg { get; set; }
public Data data { get; set; }
public int time { get; set; }
public string microtime { get; set; }
public string source { get; set; }
}
Unlike Bittrex api for example which is easier to read using JsonDotNet asset from unity store and :
BittrexJsonUrl = "https://api.bittrex.com/api/v1.1/public/getticker?market=USDT-" + coinName;
and then I use this code to get the data:
private IEnumerator GetData()
{
/////bittrex
UnityWebRequest request = UnityWebRequest.Get(BittrexJsonUrl);
yield return request.SendWebRequest();
if (request.error == null)
{
Bittrex_proccessJsonData(request.downloadHandler.text);
}
else
{
Debug.Log("Something went wrong!!");
}
}
private void Bittrex_proccessJsonData (string _url) {
var _bittrexJsonData = JsonConvert.DeserializeObject<BittrexJsonData>(_url);
bittrexPrice = _bittrexJsonData.result.Last;
}
this works perfectly with with bittrex's Json structure, since it doesnt contain the coin name all I do is change the Json URL.
Now I want to do like the same thing for BitZ's if you have any idea how to please help :) thank you in advance.
For such thing you could use good old SimpleJson.
Here you don't need to implement the entire c# structure but rather access the data field by field via it's ID. You can imagine it like a nested Dictionary like thing.
Simply create that file with given content from the link somewhere in your project and do e.g.
var json = JSON.Parse(the_JSON_string);
var usdt = json["Data"]["bst"]["usdt"].AsFloat;

JSON Newtonsoft C# - Deserialize specific fields in JSON file

I am working with a huge JSON file, where is just needed to extract some fields inside it. I've been searching some ways to deserialize, but don't want to create the whole Class and Object in C# with all the fields inside the JSON, this would be a lot of useless memory.
I can get the JSON file using a Webclient:
using (WebClient wc = new WebClient())
{
jsonWeb = wc.DownloadString("http://link_to_get_JSON");
}
//Deserialize into a JObject
JObject obj = JObject.Parse(jsonWeb);
//Tried to access the info with
var val = obj.PropTwo;
var solution = obj.Descendants().OfType<JProperty>().Where(p => p.Name == "solverSolution").Select(x => x.Value.ToString()).ToArray();
I really could not find a way to get the wanted fields inside the JObject.
Inside the JSON, the only information is needed is the solverSolution:{} below:
{
"content":
[
{
"id":"f4d7e7f5-86ab-4155-8336-ca5f552cb3b4",
"name":"m1",
"description":"m1",
"maxHeight":2000.0,
"layers":6,
"pallet":{},
"product":{},
"solverSolution":
{
"id":"106ef605-d95e-4c74-851b-63310fbcbc7d",
"name":"solver",
"maxHeight":2000.0,
"layers":6,
"solution":[
{
"X1":0,
"Y1":0,
"Z1":0,
"X2":296,
"Y2":246,
"Z2":220
},
...
"default":false
},
"customSolutions":[0]
},
{},
...
],
"pageable":{},
"totalPages":1,
"last":true,
"totalElements":7,
"first":true,
"sort":{},
"number":0,
"numberOfElements":7,
"size":20
}
Here I leave my appreciation and gratitude for the community beforehand. Cheers,
André Castro.
Then use only the desired properties in your object, making sure to follow the structure of the desired model.
public partial class RootObject {
[JsonProperty("content")]
public Content[] Content { get; set; }
}
public partial class Content {
[JsonProperty("solverSolution")]
public SolverSolution SolverSolution { get; set; }
}
public partial class SolverSolution {
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("maxHeight")]
public double MaxHeight { get; set; }
[JsonProperty("layers")]
public long Layers { get; set; }
[JsonProperty("solution")]
public Solution[] Solution { get; set; }
[JsonProperty("default")]
public bool Default { get; set; }
}
public partial class Solution {
[JsonProperty("X1")]
public long X1 { get; set; }
[JsonProperty("Y1")]
public long Y1 { get; set; }
[JsonProperty("Z1")]
public long Z1 { get; set; }
[JsonProperty("X2")]
public long X2 { get; set; }
[JsonProperty("Y2")]
public long Y2 { get; set; }
[JsonProperty("Z2")]
public long Z2 { get; set; }
}
The parser will ignore the rest that do not map to properties of the object model.
var root = Jsonsonvert.DeserializeObject<RootObject>(jsonWeb);
var solverSolution = root.Content[0].SolverSolution;
How can I get all SolverSolution
SolverSolution[] solutions = root.Content.Select(content => content.SolverSolution).ToArray();
I use:
JsonConvert.DeserializeObject<dynamic>(stringInput)
to get anonymouse type I need
Then you can use something like this to get specific part:
var obj = JsonConvert.DeserializeObject<dynamic>(input)["content"][0]["solverSolution"];
It's easy and gets me job done.
Edit:
Side note, please next time when you upload json just cut off parts that are not needed so I can serialize it, took me some time to fix it :D
You can use a JObject to parse all Json. Then, you can map a specific children to your object.
Reference

Unity JsonUtility can't reach nested variable [duplicate]

This question already has answers here:
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Closed 5 years ago.
Here is my JSON file for testing purposes :
{"timestamp":4455518515,"profileId":"5asd15qwe11as2d12q1sd","profileName":"player01",
"textures":{"material":{"url":"http://materialurl.com/aksjd54854we5a4sd.png"}}}
I want to reach textures -> material -> url but i can't. Here is my code :
public class JsonTest : MonoBehaviour {
string json;
string path;
// Use this for initialization
void Start () {
path = Application.streamingAssetsPath + "/json.txt";
json = File.ReadAllText(path);
MaterialJson test = JsonUtility.FromJson<MaterialJson>(json);
Debug.Log(test.timestamp);
Debug.Log(test.profileId);
Debug.Log(test.profileName);
Debug.Log(test.textures);
}
}
[Serializable]
public class MaterialJson
{
public double timestamp;
public string profileId;
public string profileName;
public string textures;
}
There is no problem for first three variables. But textures returns null in debug log. I have no idea how can I take nested variables in textures. Is unity's JsonUtility capable of that ? I couldn't find any info on unity's documentation.
It's worked perfectly with the code below : (Thanks to #mrfreester)
[Serializable]
public class MaterialJson
{
public double timestamp;
public string profileId;
public string profileName;
public Textures textures;
}
[Serializable]
public class Textures
{
public Material material;
}
[Serializable]
public class Material
{
public string url;
}
Debug.Log(test.textures.material.url);
It looks like you're expecting textures to be a string here
public string textures;
However it's an object in your json.
You could create a Serializable class for each associated json object if you want it to work with your json in its current format. As stated in your comment:
[Serializable]
public class MaterialJson
{
public double timestamp;
public string profileId;
public string profileName;
Textures texture;
}
[Serializable]
public class Textures
{
Material material;
}
[Serializable]
public class Material
{
public string url;
}
Which would then be accessed like this:
Debug.Log(test.texture.material.url);
Method 1:
You can use this:
using (var ms = new System.IO.MemoryStream(Encoding.Unicode.GetBytes(json)))
{
System.Runtime.Serialization.Json.DataContractJsonSerializer deserializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(MaterialJson));
MaterialJson test = (MaterialJson)deserializer.ReadObject(ms);
}
Method 2:
Thanks to Newtonsoft and JSON.Net, you can try this:
var test = JsonConvert.DeserializeObject<MaterialJson>(json);
and the MaterialJson class as below:
[Serializable]
public class MaterialJson
{
public double timestamp;
public string profileId;
public string profileName;
public texturesType textures;
public class texturesType
{
public materialType material;
public class materialType
{
public string url;
}
}
}
Now you can access the url property using: test.textures.material.url

Run code after deserialization

I'm using RestSharp to deserialize some data. This works fine and all the data loads correctly, however one of my fields is in csv format (I can't change this unfortunately) and I need to parse out that data and load it into my object.
What is a good way to structure my class so that the code inside loadData() is run when the "data" object receives a value? I basically want to avoid running loadData on its own before I can use every object, as my SkaterData object is created 20+ times.
This is what my class structure looks like:
public class SkaterData
{
public int id { get; set; }
public string data { get; set; }
public PlayerData PlayerData { get; set; }
public void loadData()
{
var dataRows = data.Split(',');
PlayerData = new PlayerData(Int32.Parse(dataRows[0]), dataRows[1], dataRows[2]);
}
}
public class PlayerData
{
public int Number { get; set; }
public string Position { get; set; }
public string Name { get; set; }
public PlayerData(int number, string position, string name)
{
this.Name = name;
this.Position = position;
this.Number = number;
}
}
Both getters and setters are functions which means that you can write something like this:
private string _data;
public int id { get; set; }
public string data
{
get { return _data; }
set
{
_data = value;
loadData();
}
}
public PlayerData PlayerData { get; set; }
public void loadData()
{
var dataRows = data.Split(',');
PlayerData = new PlayerData(Int32.Parse(dataRows[0]), dataRows[1], dataRows[2]);
}
In the above code sample, I explicitly defined the data property (which the syntax you were using is just sytantic sugar for). I then added calling loadData to the setter of this property.
Since that setter will get called on deserialization (probably) you may need a slightly different variant of what I have, but its hard to say what it would be based on your problem statement.
I am pretty sure this is what OnDeserializedAttribute is for, but I have not been able to get it to work using an XmlSerializer (edit: that's because it doesn't implement it I guess).
[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
}

Pull Properties from Class

I am trying to pull the properties from the PullConstants class to the CreateForecast class. In CreateForecast, I have created an instance of PullConstants with this code. I have also verified that both classes are in the same namespace.
PullConstants pc = new PullConstants();
However, when I try to pull a value from PullConstants to CreateForecast with code below, I always receive 0.
double sla = pc.sla;
I have verified that the value gets pulled from the database correctly, but its scope seems to not reach beyond the first run of the class. What exactly am I doing wrong that I am not able to pull the correct property value from PullConstants?
The PullConstants class is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MySql.Data.MySqlClient;
namespace ForecastBuilder
{
class PullConstants
{
InitializeDB idb = new InitializeDB();
ErrorLogger el = new ErrorLogger();
public double sla { get; set; }
public int serviceTime { get; set; }
public int avgHandleTime { get; set; }
public int maxWait { get; set; }
public double shrinkageAdjustment { get; set; }
public double alpha { get; set; }
public double beta { get; set; }
public double specialDayPerInc { get; set; }
public void PullConstantValues()
{
idb.OpenDatabases();
try
{
string sqlConstants = "select * from forecastconstants";
MySqlCommand cmdConstants = new MySqlCommand(sqlConstants, idb.myconn);
MySqlDataReader rdrConstants = cmdConstants.ExecuteReader();
while (rdrConstants.Read())
{
sla = double.Parse(rdrConstants["SLA"].ToString());
serviceTime = int.Parse(rdrConstants["ServiceTime"].ToString());
avgHandleTime = int.Parse(rdrConstants["AvgHandleTime"].ToString());
maxWait = int.Parse(rdrConstants["MaxWait"].ToString());
shrinkageAdjustment = double.Parse(rdrConstants["ShrinkageAdjustment"].ToString());
alpha = double.Parse(rdrConstants["Alpha"].ToString());
beta = double.Parse(rdrConstants["Beta"].ToString());
specialDayPerInc = double.Parse(rdrConstants["SitCallIncrPer"].ToString());
}
}
catch (Exception e)
{
el.createError(2, e.ToString(), "Could not pull constants");
}
finally
{
idb.myconn.Close();
}
}
}
}
I guess you are missing the call to PullConstantValues:
PullConstants pc = new PullConstants();
pc.PullConstantValues();
double sla = pc.sla;
If supplying these values is the only purpose of this class you may are better of using it as a constructor:
class PullConstants
{
/* ... */
public PullConstants() // instead of 'void PullConstantValues()'
{
/* ... */
}
}
If the are "real constants" may also use a singleton to not query the DB every time.
Either you are misssing a call to this function
public void PullConstantValues();
or you may consider making this function a constructor by changing it to
public PullConstant();

Categories