Json.NET serializing/ deserializing nested dictionaries - c#

I have been doing some research on this subject, and other than changing my Dictionary to a Collection of KeyValuePair objects, I have not been able to find any solution for the following case.
I have a very simple model called Client, which has a dictionary as one of it's properties and finally I am trying to serialize and deserialize a Dictionary model.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
namespace Automation
{
public class Client
{
public Client(IList<String> environments)
{
this.Environments = new Dictionary<String, Boolean>();
if (environments != null)
foreach (String environment in environments)
this.Environments[environment] = false;
}
public Dictionary<String, Boolean> Environments { get; set; }
}
public static class TestClass
{
public static void Serializer()
{
Dictionary<String, Client> newDict = new Dictionary<String, Client>();
newDict[Guid.NewGuid().ToString()] = new Client(new List<String>() { "A", "B" });
newDict[Guid.NewGuid().ToString()] = new Client(new List<String>() { "A", "B" });
newDict[Guid.NewGuid().ToString()] = new Client(new List<String>() { "A", "B" });
string json = JsonConvert.SerializeObject(newDict, Formatting.Indented);
try
{
Dictionary<String, Client> obj = JsonConvert.DeserializeObject<Dictionary<String, Client>>(json);
}
catch
{
}
}
}
}
this is the json that gets generated:
{
"246af598-89b3-4198-b2cb-9f2d8fe6e03e": {
"Environments": {
"A": false,
"B": false
}
},
"fc43bb8d-65d3-4a97-86f3-cb67567845bd": {
"Environments": {
"A": false,
"B": false
}
},
"90c0158a-69e2-42b0-9072-e695c94a2f7a": {
"Environments": {
"A": false,
"B": false
}
}
}
and here is the exception I am getting when trying to deserialize:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.IList`1[System.String]' 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 '246af598-89b3-4198-b2cb-9f2d8fe6e03e.Environments.A', line 4, position 11.
Any thoughts, eventually I will be sending this object serialized over the wire via a SignalR request.
Thank you

Just introduce a default constructor for the de-serializer to work with:
public class Client
{
public Client()
{ }
// your other constructor ...
}

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.

Deserialize a byte array containing a dictionary with JSON.Net [duplicate]

This question already has answers here:
Newtonsoft Json Deserialize Dictionary as Key/Value list from DataContractJsonSerializer
(3 answers)
Serialize dictionary as array (of key value pairs)
(6 answers)
Serialize Dictionary<,> as array in Json.NET [duplicate]
(2 answers)
Closed 4 years ago.
I am facing the following scenario. A project of mine is throwing an event that contains the following object:
public class MyEvent : BaseEvent
{
public long Id { get; set; }
public Dictionary<string, long> Pairs { get; set; }
}
I received the event and read the data as byte[] on my receiver side. The current code I have to read any generic event is:
public static T Decode(byte[] data)
{
var serializer = JsonSerializer.Create(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
using (var stream = new MemoryStream(data))
{
using (var sr = new StreamReader(stream, Encoding.UTF8))
{
var jr = new JsonTextReader(sr);
var aux = Encoding.UTF8.GetString(data);
return serializer.Deserialize(jr, typeof(T)) as T;
}
}
}
where T is my class MyEvent . Unfortunately the thrown exception is:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.Int64]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'OperationTimePairs', line 1, position 61.
The way I read it is that the object received doesn't have the correct format.. however if I try to read it through var aux = Encoding.UTF8.GetString(data); I can see that the structure is the correct one. Any idea how can I fix this? Thanks!
EDIT:
Json Example:
{
"Timestamp":"\/Date(1540996292134)\/",
"Pairs":[
{
"Key":"first time",
"Value":28
},
{
"Key":"second time",
"Value":30
},
{
"Key":"third time",
"Value":101
},
{
"Key":"operation time",
"Value":231
}
],
"Id":123637
}
I think that your classes don't match the json string structure.
Given the following json string:
{
"Timestamp":"\/Date(1540996292134)\/",
"Pairs":[
{
"Key":"first time",
"Value":28
},
{
"Key":"second time",
"Value":30
},
{
"Key":"third time",
"Value":101
},
{
"Key":"operation time",
"Value":231
}
],
"Id":123637
}
You can change your models to match the json structure, something like this:
public class MyEvent : BaseEvent
{
public long Id { get; set; }
public List<KeyValuePair<string, long>> Pairs { get; set; }
[JsonIgnore]
public Dictionary<string, long> PairsDictionary
{
get
{
if (Pairs == null)
{
return new Dictionary<string, long>();
}
return Pairs.ToDictionary(pair => pair.Key, pair => pair.Value);
}
}
}
public class BaseEvent
{
public DateTime Timestamp { get; set; }
}
Please note:
PairsDictionary is a non-serializable property based on Pairs
Given that you didn't provide the class definition of BaseEvent, I will assume that it has 1 property only
Testing the deserialization:
string json = #"{
""Timestamp"":""\/Date(1540996292134)\/"",
""Pairs"":[
{
""Key"":""first time"",
""Value"":28
},
{
""Key"":""second time"",
""Value"":30
},
{
""Key"":""third time"",
""Value"":101
},
{
""Key"":""operation time"",
""Value"":231
}
],
""Id"":123637
}";
MyEvent eventData = JsonConvert.DeserializeObject<MyEvent>(json);
Or as an alternative (using generics):
T data = JsonConvert.DeserializeObject(json, typeof(T)) as T;

Deserialize array in JSON into class/object

I'm trying to store JSON data into a class. I could deserialize my otherJSON string into class by: var ser = JsonConvert.DeserializeObject<ClsResult>(myJSON); before I got stuck with array.
{
\"Test\": [{
\"FirstBool\":1,
\"aString\":\"hello\"
}]
}
This is my class for JSON:
public class Test
{
[JsonProperty("FirstBool")]
public bool FirstBool { get; set; }
[JsonProperty("aString")]
public string aString { get; set; }
}
public class ResultObject
{
[JsonProperty("Test")]
public List<Test> Test { get; set; }
}
How I deserialize my non-array JSON:
var ser = JsonConvert.DeserializeObject<ResultObject>(myJSON);
What changes do I need to make it work again?
Edited answer
Your json string as I've noticed later contains object named Test which is basically an array of objects ( object[] ).
As you can see from the json string :
{
"Test": [{
"FirstBool" : 1,
"aString" : "hello"
}]
}
[ means that json object begins an array type and ] means that json object ended an array type.
{ means that json object begins an object type and } means that json object ended an object type.
Which in your case will require to make kind of a custom deserializer using existing methods from Newtonsoft.Json library.
Example for the Test object could be :
JObject obj = JObject.Parse(jsonString);
// now your obj contains field named "Test" that is of type object[]
// to retrieve informations you have to select "Test" token
JToken testToken = obj.SelectToken("Test");
// your token contains now something like " [{ "FirstBool" : 1, "aString" : "hello" }]"
// which basically is an array
// meaning that you have to iterate through this
foreach(var child in token.Children())
{
// and convert it to a Test object
Test test = JsonConvert.DeserializeObject<Test>(child.ToString());
// test now is fully deserialized object
}
Deserialize it as a list:
JsonConvert.DeserializeObject<List<Test>>(json);
...instead of a wrapper object.

Deserializing an anonymous top-level array in subclasses

I've got a JSON response that is contained in an outer array like this:
[
{
"type": "a"
},
{
"type": "b",
"id": 1
},
{
"type": "c",
"name": "somename"
}
]
I've tried to convert this to object like this:
class LoginOptions
{
public IList<ILoginOption> Options { get; set; }
}
interface ILoginOption
{
[JsonProperty("type")]
LoginType LoginType { get; }
}
class LoginOptionA : ILoginOption{
public LoginType LoginType
{
get { return LoginType.A; }
}
}
class LoginOptionB : ILoginOption{
public LoginType LoginType
{
get { return LoginType.B; }
}
[JsonProperty("id")]
public int Id { get; set; }
}
class LoginOptionC : ILoginOption{
public LoginType LoginType
{
get { return LoginType.C; }
}
[JsonProperty("name")]
public string Name { get; set; }
}
Which results in this exception:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'Library.Models.Domain.LoginOptions' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
I would rather not implement a collection in my LoginOptions class since that would be way too much overhead when I should be able to just store it in the field. Using the [JsonArray] attribute returns a Cannot create and populate list type Library.Models.Domain.LoginOptions.
Most resources I've found deal with a {"name":"value"} pair for the top array, not an anonymous array. How should I deserialize this?
I've changed my code accordingly:
public sealed class LoginOptions
{
[JsonConverter(typeof(LoginOptionConverter))]
public IList<ILoginOption> Options { get; set; }
}
Where my call dispatcher parses the JSON as such:
private List<ILoginOption> DeserializeObject<List<ILoginOption>>(Stream stream)
{
using (StreamReader sr = new StreamReader(stream))
using (JsonReader reader = new JsonTextReader(sr))
{
return new JsonSerializer().Deserialize<List<ILoginOption>>(reader);
}
}
And a custom converter like this:
internal class LoginOptionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ILoginOption);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = JObject.Load(reader);
var data = item["type"].Value<string>();
if (data == "UsernamePassword")
{
return item.ToObject<LoginOptionA>();
}
if (data == "Saml")
{
return item.ToObject<LoginOptionB>();
}
if (data == "Crm")
{
return item.ToObject<LoginOptionC>();
}
throw new ArgumentException("Invalid JSON response");
}
}
This throws the error
Could not create an instance of type Library.Models.Domain.ILoginOption. Type is an interface or abstract class and cannot be instantiated.
Using
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
};
as advised here did not make a difference.
Note that this error is thrown before ever making it inside the custom converter: when the JsonSerializer is created it throws this error.
Instantiation happens here:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
};
using (StreamReader sr = new StreamReader(stream))
using (JsonReader reader = new JsonTextReader(sr))
{
return JsonSerializer.Create(settings).Deserialize<List<ILoginOption>>(reader);
}
Since the top level in the JSON is an array, you should deserialize directly to a list. There is no need for a wrapper class to hold the list.
List<ILoginOption> loginOptions =
JsonConvert.DeserializeObject<List<ILoginOption>>(json);
Now, because your list will hold several different types of ILoginObjects, Json.Net will not know which ones to create as it deserializes the list. For that you will need a JsonConverter. This answer shows how you can create a converter to handle this.

JSON string is unexpectedly deserialized into object as a list

This JSON:
{
"Values": {
"Category": "2",
"Name": "Test",
"Description": "Testing",
"Expression": "[Total Items] * 100"
}
}
Is being deserialized to this DTO using JsConfig.ConvertObjectTypesIntoStringDictionary = true;:
public class MyDto {
public Dictionary<string, object> Values { get; set; }
}
Unexpectedly, the Expression property is deserialized as List<object> instead of string (see screenshot).
It seems the parser sees the opening bracket and tries to interpret the value as an array. Is there some way to turn this "type detection" off, or otherwise resolve the problem (without changing the DTO)?
I'm using ServiceStack v3.9.71.
Json.NET is able to deserialize your example JSON into an instance of your DTO class. It seems that the JSON parsing library you're using is buggy. Are you able to switch?
internal class Program
{
private static void Main()
{
const string json = #"{
""Values"": {
""Category"": ""2"",
""Name"": ""Test"",
""Description"": ""Testing"",
""Expression"": ""[Total Items] * 100""
}
}";
var myDto = JsonConvert.DeserializeObject<MyDto>(json);
}
}
public class MyDto
{
public Dictionary<string, object> Values
{
get;
set;
}
}
EDIT:
I switched my code to use ServiceStack.Text from NuGet and I was able to get the serialization to work perfectly:
var myDto = JsonSerializer.DeserializeFromString<MyDto>(json);

Categories