Error deserializing a json when an array is empty - c#

I am trying to deserialize a json but when the array ("groups") is empty it gives me an error. How can I check in Unity that if there is no data in the "groups" field a message will appear, for example?
This is the error:
InvalidCastException: Specified cast is not valid.
BC_Equipos.successCallbackGMGI (System.String jsonResponse, System.Object cbObject) (at Assets/_AG/Scripts/BC_Equipos.cs:249)
The error line 249 is: var groups = (Dictionary<string, object>[])jsonData["groups"];
When there is data in "groups" all works fine.
The json is:
{
"data": {
"requested": [],
"invited": [],
"groups": []
},
"status": 200
}
Here is how I'm deserializating:
var jsonMessage = (Dictionary<string, object>)BrainCloud.JsonFx.Json.JsonReader.Deserialize(jsonResponse);
var jsonData = (Dictionary<string, object>)jsonMessage["data"];
var groups = (Dictionary<string, object>[])jsonData["groups"];
if (groups.Length > 0)
{
Debug.Log("Groups List");
foreach (var group in groups)
{
var groupId = group["groupId"];
Debug.Log("groupId: " + groupId);
var nameGroup = group["name"];
Debug.Log("nameGroup: " + nameGroup);
}
}
else
{
Debug.Log("User hasn't joined a group already!");
}

If the array is empty, the tool can't infer what kind of array to create - typical data in the general case could, after all, be ["abc", "def"], [12, 42, 42], or - as seems to be the case - [{ }, { }]. It could even be something hybrid like ["abc", 42, {}]. You may have better luck just casting to Array - or perhaps object[], checking the length, and going from there. If object[] doesn't work: try Array, IList, or similar.
Or: a much better approach is to create a type model, and deserialize into that; then the serializer can use the defined model to interpret the data. For example:
public class SomeRoot {
public SomeData data {get;set;}
public int status {get;set;}
}
public class SomeData {
public List<Request> requested {get;} = new();
public List<Invite> invited {get;} = new();
public List<Group> groups {get;} = new();
}
public class Request {}
public class Invite {}
public class Group {}

Related

Deserializing JSON issue

I need to deserialize just part of a JSON string returned from a server. The 'myData' portion in the JSON string below.
My JSON string is structured as follows.
{
"data": {
"CODE": {
"someData": {
"h": "foo",
"id": "City",
"lat": "11.11111"
},
"feedMe": [
[
{
"myData": {
"item1": "a",
"item2": "b",
"item3": "c"
},
"moreData": {}
}
]
]
}
}
}
In Unity there is the JSONutility.FromJson method
https://docs.unity3d.com/ScriptReference/JsonUtility.FromJson.html
but unsure how I would either
1 pass only the 'myData' portion to this method.
or
2 Deserialize the entire string
An alternativ to using JsonUtility there is good old SimpleJSON which allows you to only access a certain field of your json like e.g.
var N = JSON.Parse(the_JSON_string);
var myData = N["data"]["CODE"]["feedMe"][0][0];
var item2 = myData["item2"].Value;
In general the simplest way to get the needed c# class structure for your json is always using json2csharp and make all classes [Serializable] and remove the {get; set;} in order to use fields instead of properties. Something like this
[Serializable]
public class SomeData
{
public string h;
public string id;
public string lat;
}
[Serializable]
public class CODE
{
public SomeData someData;
public List<List<MyData>> feedMe;
}
[Serializable]
public class MyData
{
public string item1;
public string item2;
public string item3;
}
[Serializable]
public class Data
{
public CODE CODE;
}
[Serializable]
public class RootObject
{
public Data data;
}
Instead of List<T> you can also use T[] if you like. And the class names actually don't matter but the structure and field names have to match.
and then use
var root = JsonUtility.FromJson<RootObject>(THE_JSON_STRING);
var myData = root.data.CODE.feedMe[0][0];
var item2 = myData.item2;
As already comented however there is a nested array in your array .. not sure if this is intended.
well, use one of the powerful json nuget -newtonsoft.json , then in your code you can iterate the values like below
var files = JObject.Parse(YourJSON);
var recList = files.SelectTokens("$..data").ToList();
foreach (JObject obj in recList.Children())
{
foreach (JProperty prop in obj.Children())
{
var key = prop.Name.ToString();
var value = prop.Value.ToString();
//Do your stuffs here
}
}
JsonUtility not work whit json files, this only for save and load basic public variables of some class. Asset Store have many frameworks for parse json. p.s. your json is strange, [] its array and you have feedMe:[[{myData, moreData}]]. One array whene just one object in array... parse confusing.

How to address an object from a json array

Using the JSON library, I've imported an array of objects:
dynamic serviceList = JsonConvert.DeserializeObject(listString)
I've got an array of json objects, each has the property "name".
[
{
"name":"abcd",
"properties":{
"type":"1234"
}
},
{
"name":"xyz",
"properties":{
"type":"aaaa"
}
}
]
How do I address just the object "abcd"?
You can parse your json Array using Newtonsoft.Json JArray.Parse() function.
Use FirstOrDefault() to get record where name is "abcd"
string listString = #"[{'name': 'abcd','properties': {'type': '1234'}},{'name': 'xyz', 'properties': { 'type': 'aaaa'}}]";
JArray jArray = JArray.Parse(listString);
//FirstOrDefault to get first record which satisfy the condition
var result = jArray.FirstOrDefault(x => (string)x["name"] == "abcd");
Console.WriteLine(result);
Output:
{
"name": "abcd",
"properties": {
"type": "1234"
}
}
.Net Fiddle
The right way to solve the problem is create a static Object
Public Obj1 {
public string name {get;set;}
public Properties properties {get;set;}
}
Public Properties {
public string type {get;set;}
}
Then you can deserialize the JSON into a List<Obj1>, in this way you can iterate your list and find the "name":"abcd" Object. var myobj = mylist.FirstOrDefault(x=> x.name == "abcd")
You can use the dynamic to do pretty much anything you would like it to do, so nothing would stop you from evaluating your result in the following way:
dynamic selectionList = JsonConvert.DeserializeObject( json );
foreach (var item in selectionList) {
if ( string.Equals( (string)item.name, "abcd", StringComparison.OrdinalIgnoreCase ) ) {
Console.WriteLine( item );
}
}
This would work as per your original request, but I think you are making it a lot harder on yourself than need be :)
To see how this works, you could check this dotnetfiddle
I would probably create a class based on the spec, but I am assuming that properties is a dynamic list of properties and their values, meaning you would still end up with a Dictionary<string, object> in the end
You can access it like this:
for (int i = 0; i < serviceList.Count; i++)
{
if (serviceList[i].name == "abc")
{
DoSomethingWith(serviceList[i];
break;
}
}
Edit: didn't see that you wanted the "abc" element, so modified the code accordingly.
This will get the value for you as a JObject, from which you can access the contained values:
var serviceList = JArray.Parse(listString);
var target = serviceList.Single(s => s["name"].ToString() == "abcd");
if (target != null)
{
var type = target["properties"]["type"];
// etc
}

How can I return my object property name as node name

I am stuck while returning api result, I have a class like
public partial class Sample
{
[JsonProperty("classificator")]
public List<Classificator> Classificator { get; set; }
}
public partial class Classificator
{
[JsonProperty("Value")]
public string Value { get; set; }
[JsonProperty("Description")]
public string Description { get; set; }
}
Let's say GetJson method retrieve our data from the database, there are 2 records and the data like
-- Value - Description
1- A - AXA
2- B - BXA
response = GetJson(); // this method gets data from db
return Content(HttpStatusCode.OK, response);
when I return this, it's like
{
"classificator": [{
"Value": "A",
"Description": "AXA"
}, {
"Value": "B",
"Description": "BXA"
}
]
}
but I would like to see like, I want to see bellowing result;
{
"classificator": [{
"A": "AXA"
}, {
"B" : "BXA"
}
]
}
I would like to ask you maybe someone knows a good practice or document(tutorial) about it.
I solve it by using, Dictionary < string, model >
but I need to return a huge nested field I cant implement this solution for all different nodes.
I solved by using Dictionary< string, object >
I put 2 nested objects inside of an object value. In my case it looked some complex I refactor and try to work through readable dictionary hierarchy.
basically for this example it something like below,
Dictionary<string, object> fooDict = new Dictionary<string, object>();
fooDict.Add("A", "AXA"); // in my case I put 2 nested object to value field
fooDict.Add("B", "BXA");
var serializedObject = JsonConvert.SerializeObject(fooDict);
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\xxx\result.txt", true))
{
file.WriteLine(serializedObject);
}

Error when deserializing JSON to Object

I need to convert JSON data that I get from a REST API and convert them to CSV for some analytic. The problem is that the JSON data do not necessarily follow the same content, so I can't define a type for mapping. This has become a challenge that is taking too much of my time. I have already created some code, but of course it is not working as it throws exception on this line
var data = JsonConvert.DeserializeObject<List<object>>(jsonData);
The error is:
Additional information: Cannot deserialize the current JSON object
(e.g. {"name":"value"}) into type
'System.Collections.Generic.List`1[System.Object]' because the type
requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'data', line 2, position 10.
please let me know what I can do to get this going.
A sample of data would be like this, the fields of data can change very often, for example a new field can be added the next day, so I don't have the liberty to create a .Net class to map the data.
{
"data": [
{
"ID": "5367ab140026875f70677ab277501bfa",
"name": "Happiness Initiatives - Flow of Communication/Process & Efficiency",
"objCode": "PROJ",
"percentComplete": 100.0,
"plannedCompletionDate": "2014-08-22T17:00:00:000-0400",
"plannedStartDate": "2014-05-05T09:00:00:000-0400",
"priority": 1,
"projectedCompletionDate": "2014-12-05T08:10:21:555-0500",
"status": "CPL"
},
{
"ID": "555f452900c8b845238716dd033cf71b",
"name": "UX Personalization Think Tank and Product Strategy",
"objCode": "PROJ",
"percentComplete": 0.0,
"plannedCompletionDate": "2015-12-01T09:00:00:000-0500",
"plannedStartDate": "2015-05-22T09:00:00:000-0400",
"priority": 1,
"projectedCompletionDate": "2016-01-04T09:00:00:000-0500",
"status": "APR"
},
{
"ID": "528b92020051ab208aef09a4740b1fe9",
"name": "SCL Health System - full Sitecore implementation (Task groups with SOW totals in Planned hours - do not bill time here)",
"objCode": "PROJ",
"percentComplete": 100.0,
"plannedCompletionDate": "2016-04-08T17:00:00:000-0400",
"plannedStartDate": "2013-11-04T09:00:00:000-0500",
"priority": 1,
"projectedCompletionDate": "2013-12-12T22:30:00:000-0500",
"status": "CPL"
}
]
}
namespace BusinessLogic
{
public class JsonToCsv
{
public string ToCsv(string jsonData, string datasetName)
{
var data = JsonConvert.DeserializeObject<List<object>>(jsonData);
DataTable table = ToDataTable(data);
StringBuilder result = new StringBuilder();
for (int i = 0; i < table.Columns.Count; i++)
{
result.Append(table.Columns[i].ColumnName);
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < table.Columns.Count; i++)
{
result.Append(row[i].ToString());
result.Append(i == table.Columns.Count - 1 ? "\n" : ",");
}
}
return result.ToString().TrimEnd(new char[] {'\r', '\n'});
}
private DataTable ToDataTable<T>( IList<T> data )
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
DataTable table = new DataTable();
for (int i = 0 ; i < props.Count ; i++)
{
PropertyDescriptor prop = props[i];
table.Columns.Add(prop.Name, prop.PropertyType);
}
object[] values = new object[props.Count];
foreach (T item in data)
{
for (int i = 0 ; i < values.Length ; i++)
{
values[i] = props[i].GetValue(item);
}
table.Rows.Add(values);
}
return table;
}
}
}
The real issue here is that you are trying to deserialize into a List<object> but your JSON actually represents a single object containing a data property which then contains a list of objects. That is why you are getting this error. Json.Net can't deserialize a single object into a list. I think what you really want to do is define a container class like this:
class Root
{
public List<Dictionary<string, object>> Data { get; set;}
}
Then deserialize like this:
var data = JsonConvert.DeserializeObject<Root>(jsonData).Data;
You will then end up with a list of dictionaries, where each dictionary represents one item in the JSON array. The dictionary key-value pairs are the dynamic values in each item. You can then work with these as you would with any other dictionary. For example, here is how you would dump out all the data:
foreach (var dict in data)
{
foreach (var kvp in dict)
{
Console.WriteLine(kvp.Key + ": " + kvp.Value);
}
Console.WriteLine();
}
Fiddle: https://dotnetfiddle.net/6UaKhJ
What you're looking for is the dynamic type. Though unrelated, this answer contains much of the information on how you'll be able to iterate through the changing properties on your object.
You will need to add some additional work to figure out how to handle your result when it is an array versus a single object as your error shows us. However, this is a good first step for you.
Basically, a dynamic object is a Dictionary, much like how a JSON object is treated in JavaScript. You just need to iterate through each of the KeyValuePair objects within the main object and go through their properties.
var data = JsonConvert.DeserializeObject<dynamic>(jsonData);
var rows = new List<string>();
// Go through the overall object, and get each item in
// the array, or property in a single object.
foreach (KeyValuePair<string, object> item in data)
{
dynamic obj = item.Value;
var row = "";
// Perhaps add a check here to see if there are more
// properties (if it is an item in an array). If not
// then you are working with a single object, and each
// item is a property itself.
foreach (KeyValuePair<string, object> prop in obj)
{
// Very dummy way to demo adding to a CSV
string += prop.Value.ToString() + ",";
}
rows.Add(string);
}
This is far from a complete example, but we don't have enough information to go on to help you finish what you're trying to do.
You are trying to deserialize into a List but your JSON actually represents a single object containing a data property containing list of objects. That is why you are getting this error. Json.Net can't deserialize a single object into a list.
Please try this:Create a class which contain single property on datatype Object and pass this class for deserialization.
class Parent
{
public object Data { get; set;}
}
Then deserialize like this:
var output = JsonConvert.DeserializeObject<Parent>(jsonData);
Try using this class instead of Object
public class Datum
{
public string ID { get; set; }
public string name { get; set; }
public string objCode { get; set; }
public double percentComplete { get; set; }
public string plannedCompletionDate { get; set; }
public string plannedStartDate { get; set; }
public int priority { get; set; }
public string projectedCompletionDate { get; set; }
public string status { get; set; }
}
public class RootObject
{
public List<Datum> data { get; set; }
}
Change to this:
var data = JsonConvert.DeserializeObject<RootObject>(jsonData);
If your data is dynamic so try a dynamic list:
using System.Web.Script.Serialization;
JavaScriptSerializer jss = new JavaScriptSerializer();
var d=jss.Deserialize<dynamic>(str);
Since you're trying to deserialize an object type into a list type, it won't deseralize directly.
You can do this:
var data = JsonConvert.DeserializeObject<ObjectDataList>(jsonData);
var rows = new List<DeserializedData>();
foreach (dynamic item in data)
{
var newData = new DeserializedData();
foreach (dynamic prop in item)
{
var row = new KeyValuePair<string, string>
(prop.Name.ToString(), prop.Value.ToString());
newData.Add(row);
}
rows.Add(newData);
}
Here are new classes
//class for key value type data
class DeserializedData
{
List<KeyValuePair<string, string>> NewData =
new List<KeyValuePair<string, string>>();
internal void Add(KeyValuePair<string, string> row)
{
NewData.Add(row);
}
}
[DataContract]
class ObjectDataList
{
[DataMember(Name ="data")]
List<object> Data { get; set; }
public IEnumerator<object> GetEnumerator()
{
foreach (var d in Data)
{
yield return d;
}
}
}
As far as I can tell, more recent versions of Newtonsoft can actually do this now, no additional work required.
I was working with the binary version however, and this did still have the issue - I had a test where you could configure to use binary or json, and the json version worked just fine, but the binary complained about not getting an array type.
I started using the BsonDataReader for this, and was looking through the properties and methods on it to see how I could best look at the contents, and lo and behold, this had a property called:
reader.ReadRootValueAsArray
Setting this to 'true' did the trick.

Decode nested JSON objects in C#

I am trying to convert a nested JSON object on an ASP.NET server. The incoming JSON string looks something like this -
data: {
user_id: 1,
taskid: "1234",
list: {
"item-1": { one: 1, two: 2 },
"item-2": { one: 1, two: 2 }
//.. where number of items is unknown
}
}
I have tried to decode the data using JSON.Decode this way
public class Data {
public int user_id { get; set; }
public string taskid { get; set; }
public List<object> list { get; set; }
}
public class DataList {
List<Data> data { get; set; }
}
// if isPost etc..
var decodedData = JSON.Decode<DataList>(Request["data"])
But when I try and iterate over decodedData I am getting an error -
foreach statement cannot operate on variables of type
'ASP._Page_that_cshtml.DataList' because
'ASP._Page_that_cshtml.DataList' does not contain a public definition
for 'GetEnumerator'
When I try casting the decodedData to a List this way -
List<Data> decodedData = JSON.Decode<DataList>(Request["data"])
I throw another error
CS0030: Cannot convert type 'ASP._Page_that_cshtml.DataList' to 'System.Collections.Generic.List<ASP._Page_that_cshtml.DataList>'
Could you please suggest an appropriate method to convert nested JSON objects into a C# object and iterating over it?
PS: trailing semi-colons omitted on-purpose
List<Data> decodedData = JSON.Decode<DataList>(Request["data"])
Should Be
var decodedData = JSON.Decode<List<Data>>(Request["data"])
var myDataList = new DataList() { data = decodedData; }
Your example is not valid json. You should have a collection [] for list:
data: {
"user_id": 1,
"taskid": "1234",
"list": [
{
"one": 1,
"two": 2
},
{
"one": 1,
"two": 2
}
]
}
The first error you are getting is quite right, your class DataList does not contain a definition for GetEnumerator which is required for a foreach statement. You will need to iterate over the property, so iterate over decodedData.data.
The second error is again correct, as you are trying to cast DataList to a type of List, something C# has no idea how to do. You would again need to create your DataList, then set the property data to the type List.
Try iterating over decodedData.data instead.
decodedData is a DataList, and the DataList class has a member data which is a List<Data>. List<Data> has a GetEnumerator method, as required by foreach.
Alternatively, you could add a GetEnumerator method to DataList:
public IEnumerator GetEnumerator() { return data.GetEnumerator(); }
You can try decoding the JSON into an array of Data objects, and then calling the ToList() extension method on that array.
var dataArray = JSON.Decode<Data[]>(Request["data"]);
var list = dataArray.ToList();

Categories