Deserializing JSON array with Unity JsonUtility fails to get serialized values - c#

I'm trying to deserialize card information from a JSON to be used in a Unity game, but it's not working quite right. I've tested the classes I'm trying to deserialize to, and I can manually create objects for them, but the deserializer doesn't create them correctly.
When I run the deserializing logic, the resulting array is the correct size, but none of the Cost or Name fields of the cards fill in and I'm left with an array of uninitialized Card objects.
The relevant code I have is as follows:
The file where we deserialize, Game.cs:
using System.IO;
using UnityEngine;
public class Game {
private CardsCollection allCards;
private void LoadJson()
{
...
// Find the path to our JSON file
// Save the path as "path"
// I have verified this line gets the correct json data as a string
string json = File.ReadAllText(path);
// Convert the json string into a deserialized array of objects
// using the CardsCollection wrapper
allCards = JsonUtility.FromJson<CardsCollection>(json);
}
}
The Card object file, Card.cs:
using System;
[Serializable]
public class Card
{
public string Name { get; set; }
public int Cost { get; set; }
public Card(string Name, int Cost)
{
this.Name = Name;
this.Cost = Cost;
}
}
[Serializable]
public class CardsCollection
{
public Card[] cards;
}
And finally the JSON itself:
{
"cards": [
{
"Name": "copper",
"Cost": 0
},
{
"Name": "silver",
"Cost": 3
},
{
"Name": "gold",
"Cost": 6
},
{
"Name": "curse",
"Cost": 0
},
{
"Name": "estate",
"Cost": 2
},
{
"Name": "duchy",
"Cost": 5
},
{
"Name": "province",
"Cost": 8
}
]
}

The Json serialization can only handle fields (see supported types https://docs.unity3d.com/Manual/JSONSerialization.html) but your Name and Cost look like properties What is the difference between a field and a property?
Since they are marked public and can be accessed directly anyway I would just remove the {get; set}

Related

How can I access my nested data using variables C#

I have a json file and I deserialised it as shown in the code below. Some context, dex is a robot and it has information such as battery and so on as shown in the json file below. I want to read the battery status for the robot that the user selected ( robot).
This is my code, currently im only accessing data.dex but i want to change it to whichever data.robot, where robot is a variable
var data = JsonConvert.DeserializeObject<Root>(json);
var robot = queryDetails2.Amr;
var text =$"";
if (data.dex.battery.status == "good")
{
text = $"{queryDetails2.Amr}'s battery is in good condition. ";
}
This is the json file:
{
"dex":
{
"current_job":
{"job":null, "task": null, "location": null},
"battery":
{"name":"battery", "status": "good", "value": "100"},
},
"dex_1":
{
"current_job":
{"job":null, "task": null, "location": null},
"battery":
{"name":"battery", "status": "good", "value": "100"},
},
"dex_2":
{
"current_job":
{"job":null, "task": null, "location": null},
"battery":
{"name":"battery", "status": "good", "value": "100"},
}
}
I wanted to use the GetMethod or the solution as suggested in this question (https://stackoverflow.com/questions/53076743/how-to-access-variable-of-a-nested-functions-in-python[1]).
However, im getting errors like it does not have a method. Now im confused, was it because i used var? but the deserialised method converts the json to an object though..
Anyway, how should i approach this?
Assuming that you have 3 robots with different names: dex, dex_1 and dex_2, you should reorganize your solution to treat the json data as a list of Robots instead of 3 separate variables for each robot.
To do this, first your json should look like this:
{
"robots":[
{
"name":"dex",
"current_job":{
"job":null,
"task":null,
"location":null
},
"battery":{
"name":"battery",
"status":"good",
"value":"100"
}
},
{
"name":"dex_1",
"current_job":{
"job":null,
"task":null,
"location":null
},
"battery":{
"name":"battery",
"status":"good",
"value":"100"
}
},
{
"name":"dex_2",
"current_job":{
"job":null,
"task":null,
"location":null
},
"battery":{
"name":"battery",
"status":"good",
"value":"100"
}
}]
}
Next, update your serialization classes. Add a field called name in the Robot class or whatever class type you currently have for data.dex. In Root, remove the "dex" fields and add a List<Robot>.
public class Root
{
public List<Robot> robots { get; set; }
}
public class Robot
{
public string name { get; set; }
public Job current_job { get; set;}
public Battery battery{ get; set; }
}
Now you can write whatever logic to get the right robot data. Here is an example using System.Linq:
var robotName = "dex_2";
var robotInfo = data.robots.First(x => x.name.Equals(robotName));
var text = $"{robotName}'s battery is in {robotInfo.battery.status} condition.";

Sort Dynamic Object by DateTime c#

From an API response, I get a JArray result:
dynamic Backups = JArray.Parse(result.Content.ReadAsStringAsync().Result.ToString());
Backups variable holds the result for me as dynamic. Here is an example of the result I get:
[{
"bu_region": "US",
"created_at": "2022-01-04 00:06:09",
"is_automate": "0",
"bu_snapshot_name": "null",
"plugin_v": "4.5.2",
},
{
"bu_region": "US",
"created_at": "2022-02-20 00:07:55",
"is_automate": "0",
"bu_snapshot_name": "null",
"plugin_v": "4.5.2",
},
{
"bu_region": "US",
"created_at": "2021-12-31 00:05:31",
"is_automate": "0",
"bu_snapshot_name": "null",
"plugin_v": "4.5.2",
}]
I would like to sort the Dynamic Result above by DateTime, and then, I show the result in my form application,
On the form after adding the above result data to an Object, I can sort it using below code:
List<Cards> sortedListByDate = listCards.OrderBy(s => s.DateOfCreation.Date).ToList();
Cards, is my Custom Class and it inherits from Panel Object/Class
namespace WinFormsApp1
{
public class Cards : Panel
{
public DateTime DateOfCreation { get; set; }
public TimeSpan TimeOfCreation { get; set; }
public Cards()
{
}
}
}
Sorting Cards object works perfectly, however, due to the dynamic functions I use, I have lot of trouble adding some handlers, and would like to sort the API result before adding its data to the Cards Object in my AppForm.
In Short, I would like to sort the Data stored at Backups by created_at
Thank you in Advance for any input.
I found my answer after some research.
Here is an easy way that worked for me:
using System;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
string json = #"
[{
""bu_region"": ""US"",
""created_at"": ""2022-01-04 00:06:09"",
""is_automate"": ""0"",
""bu_snapshot_name"": ""null"",
""plugin_v"": ""4.5.2"",
},
{
""bu_region"": ""US"",
""created_at"": ""2022-02-20 00:07:55"",
""is_automate"": ""0"",
""bu_snapshot_name"": ""null"",
""plugin_v"": ""4.5.2"",
},
{
""bu_region"": ""US"",
""created_at"": ""2021-12-31 00:05:31"",
""is_automate"": ""0"",
""bu_snapshot_name"": ""null"",
""plugin_v"": ""4.5.2"",
}]";
JArray array = JArray.Parse(json);
JArray sorted = new JArray(array.OrderBy(obj => (DateTime)obj["created_at"]));
Console.WriteLine(sorted.ToString(Formatting.Indented));
}
}
You can test it here:
https://dotnetfiddle.net/4Y96OZ

Deserializes JSON to an object in c# using json.net

I am trying to convert my json into a rule object. I have been following this guide from Newtonsoft http://www.newtonsoft.com/json/help/html/deserializeobject.htm.
public class Rule{
public string Field { get; set; }
public string Test { get; set; }
public Rule[] Cases { get; set; }
}
public class Rules {
public List<Rule> Root{ get; set; }
}
My Json from rules.js
{
"Rules": [{
"Field": "Subject",
"Test": "^(Azure Exception)",
"Cases": [{
"Field": "Content",
"Test": "Hostname: az.....(?<Hostname>[^\n])",
"Cases": [{
"Field": "Content",
"Test": "Hostname:\\s+(?<Hostname>.*)\\s+Site name:\\s+(?<SiteName>.*)"
}]
}]
}]
}
In my main method:
String RulesFile = "cSharp/rules.js";
String Json = System.IO.File.ReadAllText(RulesFile);
Rule rule = JsonConvert.DeserializeObject<Rule>(Json);
var rules = JsonConvert.DeserializeObject<Rules>(Json);
//rule.Cases
//rule.Field
//rule.Test
//rules.Root
Console.Write(rule.Field);
I've tested my json and i can output it in my terminal. I'm unsure how to assign each field in json to my rules objects. Looking at the newtonsoft docs this should work, but I'm not getting any output.
I want to be able to print these fields out, anyone know to do it?
Cheers all in advance.
In your JSON string, the root object is an object with a Rules property. That property is an array of objects. You need to define and deserialize the root object, eg
class Rules
{
public Rule[] Rules{get;set;}
}
var rules = JsonConvert.DeserializeObject<Rules>(Json);
You can generate the DTOs requires for deserialization in Visual Studio by copying the JSON string and select Paste JSON as Classes in the Edit menu. You can also generate classes by using an online converter like json2csharp

How to access and store a particular object from a json file in c#

I am trying to make a multi-subject quiz game for children using unity3D and questions and answers are stored in a json file. Now i want to create an object named "science" and "maths" and want to store their respective questions in them and when i want to access science i could loop and find and just store the science question in my string instead of reading the whole json file.
here is my json file.
Science ={
"CourseName":"Science",
"No_Of_Ques":4,
"Ques_Data":[
{ "Quesion":"which is the biggest planet in the solar system?",
"Answer":"jupiter",
"options":["mars","earth","venus","jupiter"]
},
{ "Quesion":"How many planets are there in solar system?",
"Answer":"Eight",
"options":["Seven","Nine","Five","Eight"]
},
{ "Quesion":"which is the closest planet to the sun?",
"Answer":"mercury",
"options":["mars","saturn","venus","mercury"]
},
{ "Quesion":"How many moons does jupiter have?",
"Answer":"12",
"options":["5","13","9","12"]
}
]
}
and this is how i have been acessing it so far
path = Application.dataPath + "/QnA.json";
string json = File.ReadAllText(path);
Course c1 = JsonUtility.FromJson<Course>(json);
return c1;
Course and needed serializable Classes:
[Serializable] public class Course
{
public string CourseName;
public string No_Of_Ques;
public QnA[] Ques_Data;
}
[Serializable]
public class QnA
{
public string Quesion;
public string Answer;
public string[] options;
}
i have tried so many things like Deserialization and Jobject asset but none of them seem to work and every implementation that i have found on the internet has the json data in the same file as the c# code but i can not do that as my json contains hundreds of lines of data. kindly help me out a little.
Create a course class in which create getter and setter functions for all of your json keys, for example:
if your json file is like that:
[
{
"CourseName": "Science",
"No_Of_Ques": 1,
...
},
{
"CourseName": "Math",
"No_Of_Ques": 1,
...
}
]
then course class should be:
public class Course
{
public string CourseName { get; set; }
public int No_Of_Ques { get; set; }
}
In your main class or anywhere you can access your selected course, here i am using only 0 index of a json, you can also loop through whole json and find your desirable course.
StreamReader to read a file
convert it to json
Deserialize the json as per your course
Console it
using (StreamReader r = new StreamReader("../../../js.json"))
{
string json = r.ReadToEnd();
List<Course> ro = JsonSerializer.Deserialize<List<Course>>(json);
Console.WriteLine(ro[0].CourseName);
}
I added json file in the same dire where my mainClass file is, as StreamReader requires an absolute path therefore I used an absoulte path for my json file.
using (StreamReader r = new StreamReader("../../../js.json"))
Require Libs
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
Note: I created a console app, not sure your app type
If you want to store multiple courses in your file you will need to store them as json array (as you do with questions):
[
{
"CourseName":"Science",
"No_Of_Ques":1,
"Ques_Data":[
{
"Question":"which is the biggest planet in the solar system?",
"Answer":"jupiter",
"options":[
"mars",
"jupiter"
]
}
]
},
{
"CourseName":"Math",
"No_Of_Ques":1,
"Ques_Data":[
{
"Question":"2 + 2",
"Answer":"4",
"options":[
"4",
"0"
]
}
]
}
]
then you can deserialize them with next structure(for example):
public class Course
{
[JsonProperty("CourseName")]
public string CourseName { get; set; }
[JsonProperty("No_Of_Ques")]
public long NoOfQues { get; set; }
[JsonProperty("Ques_Data")]
public QuesDatum[] QuesData { get; set; }
}
public class QuesDatum
{
[JsonProperty("Question")]
public string Question{ get; set; }
[JsonProperty("Answer")]
public string Answer { get; set; }
[JsonProperty("options")]
public string[] Options { get; set; }
}
var courses = JsonConvert.DeserializeObject<List<Course>>(jsonString);
var course = courses.Where(...).FirstOrDefault();
Or try to use json path:
var course = JToken.Parse(jsonString)
.SelectToken("$[?(#.CourseName == 'Math')]")
.ToObject<Course>();
As for jsonString you can obtain it in any way, reading from file for example.
P.S.
There was typo "Quesion" -> "Question"
To serialize and deserialize objects you have to create a C# class (in your case should be Course class) that can be [Serializable].
First your Json should be a valid one, which it is, you can validate it here.
To serialize and deserialize you can use JsonUtility to certain point, cause it doesn't deserialize jagged arrays, complex objects etc. I recommend to use third party softwares like Newtonsoft or implement your own serialization/deserialization method for your way.
Edit:
Your JSON file should be without the "Science=" part, should look like:
{
"CourseName":"Science",
"No_Of_Ques":4,
"Ques_Data":[
{ "Quesion":"which is the biggest planet in the solar system?",
"Answer":"jupiter",
"options":["mars","earth","venus","jupiter"]
},
{ "Quesion":"How many planets are there in solar system?",
"Answer":"Eight",
"options":["Seven","Nine","Five","Eight"]
},
{ "Quesion":"which is the closest planet to the sun?",
"Answer":"mercury",
"options":["mars","saturn","venus","mercury"]
},
{ "Quesion":"How many moons does jupiter have?",
"Answer":"12",
"options":["5","13","9","12"]
}
]
}
Edit:
For your comment I think you got a misunderstood of how to handle the relation between files and variables.
You want to have (or at least it seems like this) one file for every type of course, so in this case, the text above will be your Science.json file.
When you store that information, you will do similar of what you do:
path = Application.dataPath + "/QnA.json";
string json = File.ReadAllText(path);
Course scienceCourse = JsonUtility.FromJson<Course>(json); //notice the name of the variable!
So as you can see for the variable name, you will read EVERY SINGLE JSON for every course.
The other way to do that, is to store all the courses on the same Json file, and then get them as an ARRAY of Courses -> Course[] allCourses
Using Science={...} to define your object is where you get confused about object definitions. It is not a Science object. It is a Course object.
It should be more like
{
"Courses" :
[
{
"CourseName" : "Science",
...
},
{
"CourseName" : "Maths",
...
}
]
}`.
Wrap it with:
"Quiz" :
{
"Courses" :
[
{
"CourseName" : "Science",
...
},
{
"CourseName" : "Maths",
...
}
]
}
and use
[Serializable]
public class Quiz
{
public Course[] courses;
}
To hold it as a C# object.
From here you can access your courses by quiz.Courses[0].Questions[17] or write helper methods in Quiz class to call courses by enums like quiz.GetCourse(CourseCategory.Science).Questions[8].
I also suggest using Questions instead of Question_Data. That is more object friendly and helps you semantically.
As an additional suggestion, instead of dumping all of the quiz in a single JSON, you may consider sending a single Course object depending on the course, requested using a query like http://myquizserver.com/quiz.php?course=science. Since you mentioned hundreds of lines of JSON, you may also consider getting data question by question.

retrieve item from a json file c#

{
"_id": "underscore",
"_rev": "136-824a0ef7436f808755f0712c3acc825f",
"name": "underscore",
"description": "JavaScript's functional programming helper library.",
"dist-tags": {},
"versions": {
"1.0.3": {
"name": "xxx",
"description": "xxx"
},
"1.0.4": {},
"1.1.0": {}
}
}
I would like to retrieve the latest version(1.1.0) from the json file. However, it always gives out me errors of "can not deserialize json object into type RootObject
Here is my class
public class versions
{
public string name { get; set; }
public string description { get; set; }
}
public class RootObject
{
public List<versions> vs { get; set; }
}
And here is where I used it
RootObject[] dataset = JsonConvert.DeserializeObject<RootObject[]>(json);
Any idea. Many thankx
Update:
I have updated the JSON file format, but some problem..
I think the problem is, that in JSON you have to quote all "field"/attribute names. (Thats a difference from standard Javascript-Notation, where you can have unquoted attributes).
So, your file should be like:
{
"_id" : "underscore",
"versions": {
"1.0.3" : {
"name": "xxx",
"description": "xxx"
}
}
Note that {1.0.3: { name: "xxx" } } wouldn't be valid JavaScript either since '1.0.3' is an invalid identifier in JavaScript.
Looking at the JSON in your updated answer:
{
"_id" : "underscore",
"versions": {
"1.0.3" : {
"name": "xxx",
"description": "xxx"
},
"1.0.4" : {
"name": "xxx",
"description": "xxx"
}
}
This is still Invalid JSON - you have 4 opening { and only 3 closing }
you should use http://jsonlint.com/ - to validate your JSON and ensure it is Valid
I've fixed your json in question. Now for your real question
I would like to retrieve the latest version(1.1.0) from the json file. However, it always gives out me errors of "can not deserialize json object into type RootObject
You have property names like 1.0.3 that are unknown at compile time. So you can not deserialize them to a concrete class. You should handle them dynamically.
Try this:
var versions = JObject.Parse(json)["versions"]
.Children()
.Cast<JProperty>()
.ToDictionary(c => c.Name, c => c.Value.ToObject<versions>());

Categories