Unity JsonUtility can't reach nested variable [duplicate] - c#

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

Related

deserialize integer into a string

Is there a way to deserialize an integer into a string ? I need it for compatibility reason.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Abc.Test
{
[JsonSerializable(typeof(OrderInfo), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class OrderInfoContext : JsonSerializerContext
{ }
public partial class OrderInfo
{
public string UserReference { get; set; }
}
public class Program
{
static void Main(string[] args)
{
var json = #"{""UserReference"": 123}"; // <---- how having 123 deserialize as a string?
var s = JsonSerializer.Deserialize(json, OrderInfoContext.Default.OrderInfo);
}
}
}
In some cases it can make sense to separate your serialization objects (aka DTOs) from your domain objects. This give you several benefits:
Allow your domain objects to have behavior defined, without affecting serialization.
A place to handle any complicated changes to the model without losing backwards compatibility.
Allow the serialization objects to fulfill requirements like public setters, without affecting the usage in the rest of the code.
Ex:
public class OrderInfoDTO
{
public int UserReference { get; set; }
public OrderInfo ToModel() => new OrderInfo(UserReference.ToString();
}
public class OrderInfo{
public string UserReference {get;}
public OrderInfo(string userReference) => UserReference = userReference;
}
You can use a custom converter on a property. I'll look something like:
public partial class OrderInfo
{
[JsonConverter(typeof(YourCustomConverter))]
public string UserReference { get; set; }
}

Import deserialized System.Text.Json data into current type [duplicate]

This question already has answers here:
.Net Core 3.0 JsonSerializer populate existing object
(8 answers)
Closed 12 months ago.
I want to import json data from within the class which is the target of the deserialization. Is this possible with System.Text.Json without additional mapping? Ideally I would use "this" instead of the generic type parameter. I know that is impossible, but is there a similar option? Here is my test code which works, because it creates the data object only to map it to the property. Ideally, I would not need to instantiate "Test" twice.
public class Test
{
public string? Bar { get; set; }
public void ImportJson(string payload)
{
var data = System.Text.Json.JsonSerializer.Deserialize<Test>(payload);
Bar = data?.Bar; // Don't want to map
}
}
string foo = "{ \"Bar\": \"baz\" }";
var t = new Test();
t.ImportJson(foo);
Console.WriteLine(t.Bar);
you can try something like this
string foo = "{ \"Bar\": \"baz\" }";
var t = new Test();
t.Deserialize(foo);
Console.WriteLine(t.Instance.Bar);
classes
public static class Util
{
public static void Deserialize<T>(this T obj, string json) where T : IImportJson<T>
{
obj.Instance=System.Text.Json.JsonSerializer.Deserialize<T>(json);
}
}
public class Test : ImportJson<Test>
{
public string? Bar { get; set;}
}
public interface IImportJson<T>
{
public T Instance { get; set; }
}
public class ImportJson<T>: IImportJson<T>
{
public T Instance { get; set; }
}
if class dosn't have many properies, it could be like this too
public interface IImportJson<T>
{
public void ImportJson (T obj);
}
public class Test : IImportJson<Test>
{
public string? Bar { get; set; }
public void ImportJson(Test test)
{
Bar=test.Bar;
}
}

Cannot deserialize data with JsonUtility [duplicate]

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.

c# - Unity parse nested Json [duplicate]

This question already has answers here:
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Parse nested json in unity [duplicate]
(2 answers)
Closed 5 years ago.
I have the following problem: I'm trying to parse a nested JSON using the Unity JsonUtility, but if i'm logging one of the nested parameters i get Null as a result.
Here's the Json:
{
"basic": {
"name": "Demo Bow"
},
"effect": {
"damage": {
"stages": 3,
"one": 4,
"two": 10,
"three": 40
}
}
}
And here's my code:
public class Basic
{
public string name;
}
public class Damage
{
public int stages;
public int one;
public int two;
public int three;
}
public class Effect
{
public Damage damage;
}
public class RootObject
{
public Basic basic;
public Effect effect;
}
Edit: No its not a duplicate, cause I already removed the "{ get; set; }"
And heres the missing code.
public static RootObject CreateFromJSON(string json){
RootObject weapon = JsonUtility.FromJson <RootObject> (json);
return weapon;
}
Thanks for any help
You are missing the [System.Serializable] attribute on your classes, it is needed for JsonUtility to be able to serialize or deserialize your classes.
[System.Serializable]
public class Basic
{
public string name;
}
[System.Serializable]
public class Damage
{
public int stages;
public int one;
public int two;
public int three;
}
[System.Serializable]
public class Effect
{
public Damage damage;
}
[System.Serializable]
public class RootObject
{
public Basic basic;
public Effect effect;
}

C# get children from JSON

my JSON looks like this:
{
"kind": "youtube#videoListResponse",
"etag": "\"sZ5p5Mo8dPpfIzLYQBF8QIQJym0/TOPzMxlQJUtRJBXHeKYMXsdEkHs\"",
"pageInfo":
{
"totalResults": 1,
"resultsPerPage": 1
},
"items":
[
{
"kind": "youtube#video",
"etag": "\"sZ5p5Mo8dPpfIzLYQBF8QIQJym0/BIVqr1Mkbule8othzWvZRor92wU\"",
"id": "QMNkWwq6L4Q",
"contentDetails":
{
"duration": "PT4M45S",
"dimension": "2d",
"definition": "hd",
"caption": "false",
"licensedContent": true,
"projection": "rectangular"
}
}
]
}
The formatting might be a bit off, sorry.
I tried creating a class like this:
public class VideoDetails
{
public string kind;
public string etag;
public string id;
public string duration;
public string definition;
public string caption;
public string licensedContent;
public string projection;
}
After that I deserialize the JSON file:
VideoDetailRead = JsonConvert.DeserializeObject<VideoDetails>(json);
but I only get "etag" and "kind". Nothing else. My question is: How do I read the data that's listed under "items"? This is my first time working with JSON and therefor I don't know much. Thanks for some answers.
Create a second object to model the children data. Provided the items property has also a child under contentDetails, you will also need another object to deserialize these properties.
public class VideoDetailsItem
{
public string kind;
public string etag;
public string id;
public VideoContentDetails contentDetails;
}
public class VideoContentDetails
{
public string duration;
public string definition;
public string caption;
public bool licensedContent;
public string projection;
}
And to the parent object add a List<VideoDetailsItem>.
public class VideoDetails
{
public string kind;
public string etag;
public List<VideoDetailsItem> items;
}
When deserializing JSON objects you have to mimic the JSON object structure in your object. Also, consider using properties instead of public fields to favor encapsulation in your data objects.
The properties you have listed are inside an array named items, so your object hierarchy has to match this:
public class VideoList
{
public string kind;
public string etag;
// etc...
public List<VideoDetails> items;
}
public class VideoDetails
{
public string kind;
public string etag;
public string id;
public string duration;
public string definition;
public string caption;
public string licensedContent;
public string projection;
}
Then:
var videos = JsonConvert.DeserializeObject<VideoList>(json);
This is because you aren't properly modeling the object structure. Right now, your JSON is structured like:
{baseInformation, VideoDetailsArray}
Thus you have to restructure your data object as
public class VideoDetails
{
//Base information
public string kind {get; set;}
public string etag {get; set;}
//...
public List<VideoDetails> {get; set;}
}
You have to define your class to match the structure of the JSON: in this case, you would define items as a list of a separate class which has the properties under it like duration etc. You should have your VideoDetails class nested inside another class that can wrap both objects in the JSON, and add another class for the contentDetails object.
One thing you can do is copy the JSON into your clipboard, open a new class file in Visual Studio, and go to Edit > Paste Special > Paste JSON as Classes. This will create the correct structure for you, and you can see how the nested classes in the JSON properly translate to C# classes.

Categories