This question already has answers here:
Serialize and Deserialize Json and Json Array in Unity
(9 answers)
Closed 3 years ago.
I recently got into C# using Unity. I previously worked with JavaScript and know that they defiantly have their differences, but still have some similarities. I have used JSON with JS and it works great. Now with Unity, I want to store data of upcoming "stages" in JSON in an infinite runner game. But from my experience, JSON does not work nearly as well with C# as it does with JS. I have looked at Unity's JSON Utility but haven't figured out if it's possible to simply have a string and then convert it into an object which you could access like object.item.array[0].item which is how you'd do it in JS. Another thing that I looked at was this but as a novice to C#, I couldn't make heads or tails of it. So does C# have something like JSON, but its more integrated? I've used C# lists, can you get 3D lists with items and not just arrays? I know that they are very different languages, and what works well on one, might not on another.
I think closest to what you describe in your questions and the comments as
simply convert a JSON string into a JSONObject
would maybe be the good old SimpleJSON. Simply copy the SimpleJSON.cs and depending on your needs maybe SimpleJSONUnity.cs(provides some extensions for directly parsing to and from Vector2, Vector3, Vector4, Quaternion, Rect, RectOffset and Matrix4x4) somewhere into your Assets folder.
Then given the example json
{
"version": "1.0",
"data": {
"sampleArray": [
"string value",
5,
{
"name": "sub object"
}
]
}
}
you can simply access single fields like
using SimpleJSON;
...
var jsonObject = JSON.Parse(the_JSON_string);
string versionString = jsonObject["version"].Value; // versionString will be a string containing "1.0"
float versionNumber = jsonObject["version"].AsFloat; // versionNumber will be a float containing 1.0
string val = jsonObject["data"]["sampleArray"][0]; // val contains "string value"
string name = jsonObject["data"]["sampleArray"][2]["name"]; // name will be a string containing "sub object"
...
Using this you don't have to re-create the entire c# class representation of the JSON data which might sometimes be a huge overhead if you just want to access a single value from the JSON string.
However if you want to serialze and deserialize entire data structures you won't get happy using SimpleJSON. Given the example above this is how you would use Unity's JsonUtility
Create the c# class representation of the data yu want to store. In this case e.g. something like
[Serializable]
public class RootObject
{
public string version = "";
public Data data = new Data();
}
[Serializable]
public class Data
{
public List<object> sampleArray = new List<object>();
}
[Serializeable]
public class SubData
{
public string name = "";
}
Then fill it with values and parse it to JSON like
var jsonObject = new RootObject()
{
version = "1.0",
data = new Data()
{
sampleArray = new List<object>()
{
"string value",
5,
new SubData(){ name = "sub object" }
}
}
};
var jsonString = JsonUtility.ToJson(jsonObject);
And to convert it back to c# either if jsonObject was not created yet
jsonObject = JsonUtility.FromJson<RootObject>(jsonString);
otherwise
JsonUtility.FromJsonOverwrite(jsonString, jsonObject);
JSON is just how objects are represented in JavaScript. While C# can use JSON, you'll probably have a much easier time defining your own custom classes. Using classes, you can define all the properties you'll need, string them together into a list, etc.. Hope this helps.
Related
I'm very confused about the Unity JsonUtility, mainly due to lack of c# understanding.
All their examples are with very non generic classes, but I need flexibility, and havent been able to find a good e2e example that worked for me.
I have this simple json data that I need to work with:
"{
1: 987,
2: 123,
3: 001,
4: 157,
}"
It's a key value object where both key and value are integers.
My problem is that the size of the object is unknow, the index could be ever increasing, and I do not understand how I can define a generic object (class?) in c# that JsonUtility can convert. Do I need to define a class, or can I do it inline? Do I need to define getters and setter, or are there some convenient wildcard dictionary like thingy that would be suitable?
I'm trying to achieve something like this:
string jsonString = "{1:987,2:123,3:001,4:157}":
var object = JsonUtility.FromJson<??????>(jsonString);
Debug.Log(object[1]) // 987
Debug.Log(object[99]) // undefined
And then I would like to be able to add new pair to the object and at last turn it into json again something like:
object[100] = 999;
const jsonString = JsonUtility.ToJson<??????>(object);
Debug.Log(jsonString) // "{1:987,2:123,3:001,4:157,100:999}":
KeyValuePairs or Dictionary is not supported by the built-in JsonUtility.
You can use Newtonsoft .Net JSON (there is a wrapper Package for Unity) where this would be a simple Dictionary<int, int>
var json = "{1:987,2:123,3:001,4:157}":
var values = JsonConvert.DeserializeObject<Dictionary<int, int>>(json);
Debug.Log(values [2]);
and
var values = new Dictionary<int, int>{ { 2, 123 }, {7, 345} };
var json = JsonConvert.SerializeObject(values);
Debug.Log(json);
I'm working in .Net Core 2.2 with the MVC pattern. I have a Web-API controller, where I created some Endpoints with classic CRUD-methods.
Now I have everything compiling and when I debug my soluting I get an array containing a couple of JSON-formatted objects - but the system sees the output it as an array, not as JSON.
Now I want this array to become a JSON.
I've looked at this: https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Linq_JObject.htm
My orginal method looked like this (in controller):
[HttpGet("/ListAllItems")]
public async Task<IEnumerable<DtoModel>> ListAllItemsFromDb()
{
return await _dbProvider.ListAllItemsAsync();
}
and gave an output of:
[{"id": "GUID-STRING", "itemName": "foo"}, {"id": "GUID-STRING2", "itemName": "bar"}]
//My frontend did not recognize this as a JSON, since it is an array, and threw an exception
So I tried this in my controller instead
[HttpGet("/ListAllItems")]
public async Task<JObject> ListAllItemsFromDb()
{
var result = await _dbProvider.ListAllItemsAsync();
string output = result.ToString();
string json = JsonConvert.SerializeObject(output);
JObject obj = JObject.Parse(json);
return obj;
}
When I run this code, my error message states: "JsonReaderException: Error reading JObject from JsonReader. Current JsonReader item is not an object: String. Path '', line 1, position 60."
How do I make the array of objects into a JSON object?
Your front end might reject JSON arrays because there are some security issues with them. The article Anatomy of a Subtle JSON Vulnerability explains it. But I think it is no longer applicable, see this and this Stack Overflow question
You can however create a wrapper object to avoid returning top level JSON arrays:
public class Wrapper<T> {
public T Data {get; set;}
}
Then in your controller:
[HttpGet("/ListAllItems")]
public async Task<Wrapper<IEnumerable<DtoModel>>> ListAllItemsFromDb()
{
return new Wrapper<<IEnumerable<DtoModel>>() {
Data = await _dbProvider.ListAllItemsAsync()
};
}
The MVC infraestructure will return a JSON result like this:
{
Value: [{"id": "GUID-STRING", "itemName": "foo"}, {"id": "GUID-STRING2", "itemName": "bar"}]
}
This should make your front end happy.
JsonConvert will convert your list with no problems - calling ToString() on IEnumerable<DtoObject> might be your problem.
Pass the list in to JsonConvert and it will parse correctly.
var result = await _dbProvider.ListAllItemsAsync();
string json = JsonConvert.SerializeObject(result);
I am working with Unity and Python and sending data over UDP to my Unity client. I am sending a JSON which reaches my Unity application successfully. I can't figure out how to properly parse this JSON. I have tried :
JsonUtility.FromJson<T>(...)
but the result wasn't correct. I implemented a [System.Serializable] class which contained data types
List<List<int>> landmarks;
List<double> eulerAngles;
List<List<double>> boxPoints;
And initialized these in my constructor using
landmarks = new List<List<int>>();
...
Every time I try to print the result, all my variables are empty.
My JSON looks like this -
{
"eulerAngles": [6.2255, -15.8976, -0.5881],
"boxPoints": [
[194.05965, 194.15782],
[182.35829, 189.05042],
[6 more...]
],
"landmarks": [
[196, 225],
[197, 242],
[199, 258],
[65 more..]
]
}
Any help appreciated.
Your JSON is still strangly formatted and I don't understand your data structure.
Why are you storing everything in List<int[]> while the values you get are clearly float (or double) values?
If something it should probably be
[Serializable]
public class JsonData
{
// here maybe int is ok at least from your data it seems so
public List<int[]> landmarks = new List<int[]>();
// for those you clearly want floats
public List<float[]> eulerAngles = new List<float[]>();
public List<float[]> boxPoints = new List<float[]>();
}
Have a look at this JSON to c# converter!
BUT anyway Unity's JsonUtility seems not to support the deserialization of nested arrays and lists! I guess the reason is that while other primitive data types have a default value that is different from null, List and arrays are null be default and have to be initialized before filling them. JsonUtility seems to not support that. I think it is related to
Note that while it is possible to pass primitive types to this method, the results may not be what you expect; instead of serializing them directly, the method will attempt to serialize their public instance fields, producing an empty object as a result. Similarly, passing an array to this method will not produce a JSON array containing each element, but an object containing the public fields of the array object itself (of which there are none). To serialize the actual content of an array or primitive type, it is necessary to wrap it in a class or struct. (JsonUtility.ToJson)
and
a field of type T will have value default(T) - it will not be given any value specified as a field initializer, as the constructor for the object is not executed during deserialization (JsonUtility.FromJson)
However, why even take them as arrays where every of your arrays only has one element anyway when it seems (and your names and title say) you rather want to store tuples of allways 2 and 3 numbers?
In case you can change the JSON format it seems like you rather would want something like
[Serializable]
public class JsonData
{
// here maybe int is ok at least from your data it seems so
public List<Vector2Int> landmarks = new List<Vector2Int>();
// for those you clearly want floats
public List<Vector3> eulerAngles = new List<Vector3>();
public List<Vector2> boxPoints = new List<Vector2>();
}
And your JSON should than rather look like
{
"eulerAngles": [
{"x":6.2255, "y":-15.8976, "z":-0.5881},
...
],
"boxPoints": [
{"x":194.05965, "y":194.15782},
{"x":182.35829, "y":189.05042},
...
],
"landmarks": [
{"x":196, "y":225},
{"x":197, "y":242},
{"x":199, "y":258},
...
]
}
The simplest way to see how your JSON should actually look like would be to take your JsonData class and serialize it once so you see what Unity expects as JSON.
var testData = new JsonData();
// fill it with example values
testData.eulerAngles = new List<Vector3>()
{
new Vector3(6.2255f, -15.8976f, -0.5881f)
};
testData.boxPoints = new List<Vector2>()
{
new Vector2(194.05965f, 194.15782f),
new Vector2(182.35829f, 189.05042f)
};
testData.landmarks = new List<Vector2Int>()
{
new Vector2Int(196, 225),
new Vector2Int(197, 242),
new Vector2Int(199, 258)
}
var result = JsonUtility.ToJson(testData);
If changing the JSON format is not an option you could instead have a look at SimpleJSON which doesn't deserialize directly into a class instance but rather to a JSONNode which can contain nested lists and arrays e.g.
var jsonData = JSON.Parse(yourJson);
var firstEuerAngles = jsonData["eulerAngles"][0];
I'm not sure if I'm double-serializing my JSON object, but the output results in an unwanted format. I'm exposing the REST endpoint via an ArcGIS Server Object Extension (REST SOE). I've also recently implemented JSON.Net which essentially allowed me to remove several lines of code.
So here is the handler, which is the core piece creating the data for the service (for you non GIS peeps).
private byte[] SearchOptionsResHandler(NameValueCollection boundVariables, string outputFormat, string requestProperties, out string responseProperties)
{
responseProperties = null;
JsonObject result = new JsonObject();
// Towns
DataTable dataTableTowns = GetDataTableTowns();
String jsonStringTowns = JsonConvert.SerializeObject(dataTableTowns);
result.AddString("Towns", jsonStringTowns);
// GL_Description
DataTable dataTableGLDesc = GetDataTableGLDesc();
String jsonStringGLDesc = JsonConvert.SerializeObject(dataTableGLDesc);
result.AddString("GLDesc", jsonStringGLDesc);
return Encoding.UTF8.GetBytes(result.ToJson());
}
The result is ugly scaped JSON:
{
"Towns": "[{\"Column1\":\"ANSONIA\"},{\"Column1\":\"BETHANY\"},{\"Column1\":\"BLOOMFIELD\"}]",
"GLDesc": "[{\"Column1\":\"Commercial\"},{\"Column1\":\"Industrial\"},{\"Column1\":\"Public\"}]"
}
Is it because I'm double serializing it somehow? Thanks for looking this over.
Yes, you are double serializing it. It's right there in your code.
For each of your data tables, dataTableTowns and dataTableGLDesc, you are calling JsonConvert.SerializeObject() to convert them to JSON strings, which you then add to a result JsonObject. You then call ToJson() on the result, which presumably serializes the whole thing to JSON again.
JsonObject is not part of Json.Net, while JsonConvert is, so I would recommend using one or the other. Generally, you just want to accumulate everything into a single result object and then serialize the whole thing once at the end. Here is how I would do it with Json.Net using an anonymous object to hold the result:
var result = new
{
Towns = GetDataTableTowns(),
GLDesc = GetDataTableGLDesc()
};
string json = JsonConvert.SerializeObject(result);
return Encoding.UTF8.GetBytes(json);
Here is the output:
{
"Towns":[{"Column1":"ANSONIA"},{"Column1":"BETHANY"},{"Column1":"BLOOMFIELD"}],
"GLDesc":[{"Column1":"Commercial"},{"Column1":"Industrial"},{"Column1":"Public"}]
}
First of all let me tell you one thing that I am posting this question is just for eagerness and to increase my knowledge. Hope you can clear my doubts !
Now lets come to the point
I got this question from my previous question-answer
Actually the problem is if I use
List<oodleListings> listings;
instead of
oodleListings[] listings;
It works fine !! I can deserialize my json string easily.
Now the problem is why array is not supported while deserializing json ?
What is the actual reason to use List instead of array ?
Your problem is not related with Arrays or Lists.
See the example classes below
public class TestClassList
{
public List<User> users;
}
public class TestClassArray
{
public User[] users;
}
public class User
{
public string user;
}
and assume your input string is
string json1 = "{ users:[{user:'11'},{user:'22'}] }";
var obj1= ser.Deserialize<TestClassList>(json1);
var obj2 = ser.Deserialize<TestClassArray>(json1);
both deserializations will work..
But if you try to deserialize this string string json2 = "{ users:{user:'11'} }";
var obj3 = ser.Deserialize<TestClassList>(json2);
var obj4 = ser.Deserialize<TestClassArray>(json2); //<--ERROR
you will get error in the second line (Althoug first line doesn't give an error, it doesn't return a valid object either).
As a result: The second json string does not contain an array of users, this is why you get No parameterless constructor defined for type of 'User[]'.
List<T> does not have a fixed length, so you are free to Add items to that object, without knowing its size. List is definitely the more flexible/functional class, but as a side-effect, it has a larger memory footprint.
Also, an object of type List<oodleListing> will have a method AddRange method that takes a parameter of oodleListing[] so you could always deserialize and then add to your generic class.
There is nothing wrong with Array or List. I just tried your code and it works without any issue for both Array and List.
In your previous question you didn't give the JSON serialized string, other wise it would have been solved there it self. You can check this post from ASP.Net.