Serialize complex dictionary with DataContractJsonSerializer - c#

With .NET 4.5 version of DataContractJsonSerializer and with help of DataContractJsonSerializerSettings.UseSimpleDictionaryFormat I can serialize dictionaries. So for example this dictionary:
var dic = new Dictionary<string, object>
{
{ "Level", 3 },
{ "Location", "Catacomb" }
};
will be converted into nice JSON:
{
"Level":3,
"Location":"Catacomb"
}
But if I have another dictionary as value:
var dic = new Dictionary<string, object>
{
{ "Level", 3 },
{ "Location", new Dictionary<string, object>
{
{ "Name", "Catacobms" }
}
}
};
the the resulted JSON looks really bad:
{
"Level":3,
"Location":[
{
"__type":"KeyValuePairOfstringanyType:#System.Collections.Generic",
"key":"Name",
"value":"Catacobms"
}
]
}
Is there any way to fix this problem?
PS: I know that there are other good JSON serializers, but in this case I need to use DataContractJsonSerializer.

Try setting the EmitTypeInformation property on the serializer to EmitTypeInformation.Never
See MSDN Entry

Related

C# Json To Dictionary of Objects

I want to take some Json and parse it in to a collection of key/value pairs, but some of the values will be dictionaries themselves. I tried the usual Newtonsoft deserialization. It's close, but not quite right. The end result must be a dictionary, not a strongly typed class.
This is some example Json:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job"
}
]
}
This is the code I used to deserialize:
var json = File.ReadAllText(#"c:\temp\job.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
The result is almost correct, but the value of the item with a key of "JobNotes" is just json string. I want the parser to recurse in and deserialise the inner Json to a further dictionary of strings and objects. Is there a way I can do this with the Newtonsoft library? Or, is there another library that will do the trick? Can I hook in to the parsing method to override the functionality at that point in time?
This is a modified version of #DanielKeogh's code. It works well.
class Program
{
static void Main(string[] args)
{
var json = File.ReadAllText(#"c:\temp\job3.json");
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
RecurseDeserialize(result);
}
private static void RecurseDeserialize(Dictionary<string, object> result)
{
//Iterate throgh key/value pairs
foreach (var keyValuePair in result.ToArray())
{
//Check to see if Newtonsoft thinks this is a JArray
var jarray = keyValuePair.Value as JArray;
if (jarray != null)
{
//We have a JArray
//Convert JArray back to json and deserialize to a list of dictionaries
var dictionaries = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jarray.ToString());
//Set the result as the dictionary
result[keyValuePair.Key] = dictionaries;
//Iterate throught the dictionaries
foreach (var dictionary in dictionaries)
{
//Recurse
RecurseDeserialize(dictionary);
}
}
}
}
}
This modified Json shows how deep it goes:
{
"JobNumber": 1010,
"Asset": null,
"JobNotes": [
{
"NoteText": "It's not working.",
"NoteType": "Complaint"
},
{
"NoteText": "Needs to be fixed",
"NoteType": "Job",
"JobNoteNotes": [
{
"Something": 1,
"Something2": "Test"
}
]
}
]
}
The result ends three dictionaries deep so that I can get at the "Something" value by key.
This can be done with a little recursion. I'll leave defining IsJson up to you as an academic exercise. :)
Dictionary<string, object> RecursiveDeserialize(string json)
{
var result = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var pair in result.ToArray())
{
if(IsJson(pair.Value))
{
result[pair.Key] = RecursiveDeserialize(pair.Value);
}
}
return result;
}
Using this object for json string
public class JobNote
{
public string NoteText { get; set; }
public string NoteType { get; set; }
}
public class ListJob
{
public int JobNumber { get; set; }
public object Asset { get; set; }
public List<JobNote> JobNotes { get; set; }
}
Then you can deserialize it

JSON.NET serialization of System.Drawing.Color with TypeNameHandling

I want to serialize a Dictionary<string, object> that may contain System.Drawing.Color values or other types as values. I create a serializer with TypeNameHandling.Auto, and this works for most classes, but not Color.
Example code: (DotNetFiddle: https://dotnetfiddle.net/UvphQO)
public class Program
{
class A { }
class B { }
public static void Main()
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["Foo"] = new A();
dict["Bar"] = new B();
dict["Baz"] = new object();
dict["Rainbow"] = Color.FromArgb(20, 20, 20, 20);
var ser = new JsonSerializer
{
TypeNameHandling = TypeNameHandling.Auto
};
var w = new JsonTextWriter(Console.Out)
{
Formatting = Formatting.Indented
};
ser.Serialize(w, dict);
}
}
The resulting output:
{
"Foo": {
"$type": "Program+A, mi1i2eqo"
},
"Bar": {
"$type": "Program+B, mi1i2eqo"
},
"Baz": {},
"Rainbow": "20, 20, 20, 20"
}
As expected, instances of A or B in the dictionary have the needed $type metadata for reconstruction. But instances of Color do not. When this json is deserialized, dict["Rainbow"] is a System.String.
There are many ways I could work around this, but the preferred solution would be to figure out why the serializer is doing something seemingly incorrect with $type in this case.
The problem is that System.Drawing.Color has an associated TypeConverter, which Json.Net uses to convert the type to a string value and back. However, since the output value is a string (not an complex object), Json.Net does not output any type information for it. This causes an issue during deserialization if your property is defined as object instead of strongly typed: Json.Net won't be able to find the TypeConverter, so it will not be able to convert the string value back into a Color.
One way to work around the issue is to wrap the Color in another class which has a strongly-typed property for the color value.
class ColorHolder
{
public System.Drawing.Color Value { get; set; }
}
Json.Net will then write out type information for the wrapper class, which will allow the nested Color struct to be deserialized correctly. Here is a round-trip demo:
class Program
{
static void Main(string[] args)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["Rainbow"] = new ColorHolder { Value = Color.FromArgb(10, 20, 30, 40) };
JsonSerializerSettings settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(dict, settings);
Console.WriteLine(json);
Console.WriteLine();
var d = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);
ColorHolder holder = (ColorHolder)d["Rainbow"];
Console.WriteLine("A=" + holder.Value.A);
Console.WriteLine("R=" + holder.Value.R);
Console.WriteLine("G=" + holder.Value.G);
Console.WriteLine("B=" + holder.Value.B);
}
}
Output:
{
"Rainbow": {
"$type": "JsonTest.ColorHolder, JsonTest",
"Value": "10, 20, 30, 40"
}
}
A=10
R=20
G=30
B=40
You can serialize and deserialize Color as Int32.
For instance, I do that:
private Color _TextColor;
public Color TextColor
{
get => _TextColor;
set
{
if (_TextColor != value)
{
_TextColor = value;
PropertyChangedCall("StyleProperty");
}
}
}
public int TextColorValue
{
get => _TextColor.ToArgb();
set
{
if (_TextColor != Color.FromArgb(value))
{
_TextColor = Color.FromArgb(value);
PropertyChangedCall("StyleProperty");
}
}
}
The System.Drawing.Color is a structure, and not a class in .NET. That sounds like an answer to your problem. Try to wrap it up with a class, or create your own color class and it should solve it.

Serialize a Dictionary as JSON object using DataContractJsonSerializer

I have an [DataContract] object that has some properties and is serialized to JSON using DataContractJsonSerializer.
One of the properties is of type Dictionary<string, string> and when the serialization happens it produces the following JSON schema.
"extra_data": [
{
"Key": "aKey",
"Value": "aValue"
}
]
Now I need the JSON schema to be like this
"extra_data": {
"aKey": "aValue"
}
You can never of course know before what the values are, it is a Dictionary that the user will set keys and values.
I'm thinking if this can happen using anonymous types, or is there a configuration I can take to accomplish my goal?
Thank you.
Ok, let say you have:
[DataContract]
public class MyObject
{
[DataMember(Name = "extra_data")]
public Dictionary<string, string> MyDictionary { get; set; }
}
Then you can use DataContractJsonSerializerSettings with UseSimpleDictionaryFormat set to true, something like this:
var myObject = new MyObject { MyDictionary = new Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } } };
DataContractJsonSerializerSettings settings = new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true };
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(MyObject), settings);
var result = string.Empty;
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, myObject);
result = Encoding.Default.GetString(ms.ToArray());
}

Serialize dictionary as array (of key value pairs)

Json.Net typically serializes a Dictionary<k,v> into a collection;
"MyDict": {
"Apples": {
"Taste": 1341181398,
"Title": "Granny Smith",
},
"Oranges": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
},
}
Which is great. And from looking around on SO it seems to be what most people want. However, in this particular case, I want to serialize between my Dictionary<k,v> and the Array format instead;
"MyDict": [
"k": "Apples",
"v": {
"Taste": 1341181398,
"Title": "Granny Smith",
}
},
"k:": "Oranges",
"v:": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
}
},
]
Is there an easy way to do this with my existing field type? Is there an attribute I can annotate for instance?
Another way to accomplish this is to use a custom ContractResolver. That way you do not have to subclass Dictionary<K,V> nor apply a transform each time you serialize, as suggested in other answers.
The following resolver will cause ALL dictionaries to be serialized as an array of objects with "Key" and "Value" properties:
class DictionaryAsArrayResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (objectType.GetInterfaces().Any(i => i == typeof(IDictionary) ||
(i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))))
{
return base.CreateArrayContract(objectType);
}
return base.CreateContract(objectType);
}
}
To use the resolver, add it to your JsonSerializerSettings, then pass the settings to JsonConvert.SerializeObject() like this:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new DictionaryAsArrayResolver();
string json = JsonConvert.SerializeObject(obj, settings);
Here is a working demo.
Ah, it turns out this is as straightforward as I'd hoped. My Dictionary<k,v> is subclassed already and I found that I can annotate it with [JsonArrayAttribute]. That gives me exactly the format I need;
"MyDict": [
{
"Key": "Apples",
"Value": {
"Taste": 1341181398,
"Title": "Granny Smith",
}
},
{
"Key:": "Oranges",
"Value:": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
}
},
]
For this example, I'll use the dictonary:
var myDict = new Dictionary<string,string>() {
{"a","123"},
{"b","234"},
{"c","345"}
};
which serializes (with Newtonsoft.Json.JsonConvert.SerializeObject(myDict)) to:
{"a":"123","b":"234","c":"345"}
You could do a transform using LINQ to create an anonymous object, and serialize that:
var myDictTransformed = from key in myDict.Keys
select new { k = key, v = myDict[key] };
Or you could use a real object
class MyDictEntry
{
public string k { get; set; }
public string v { get; set; }
}
and either the above or the alternative LINQ syntax:
var myDictTransformed = myDict.Keys.AsEnumerable()
.Select(key => new MyDictEntry{
k = key,
v = myDict[key]
});
Either way, this serializes to:
[
{"k":"a", "v":"123"},
{"k":"b", "v":"234"},
{"k":"c", "v":"345"}
]
.NET Fiddle link: https://dotnetfiddle.net/LhisVW
The simplest solution I found is to convert your Dictionary<string, string> to a List<KeyValuePair<string, string>>. JSON.NET then converts your List into an array of objects with the form { Key: 'keyname', Value: 'value' }. This works well if you accept the required model change and don't want to subclass your Dictionary.
gregmac's answer was helpful, but didn't quite work. The following is the same idea... without the puns.
var dictionaryTransformed = dictionary.Select(item => item.Key).Select(i =>
new {Key = i, Value = dictionary[i] });
or of course
var dictionaryTransformed = dictionary.Select(item =>
new {item.Key, Value = dictionary[item.Key] });
Then to json
var json = (new JavaScriptSerializer()).Serialize(
new { Container = dictionaryTransformed.ToArray() } )
I'm not exactly sure why, but the custom ContractResolver by Brian Rogers listed above didn't work for me. It seemed to get into an endless loop somewhere internally. Possibly due to other parts of my json.net setup.
Anyway - this workaround did the trick for me.
public interface IStrongIdentifier
{
string StringValue { get; set; }
}
public class StrongIdentifierKeyedDictionaryWrapper<TKey, TValue> : Dictionary<string, TValue>
where TKey : IStrongIdentifier
{
public void Add(TKey key, TValue value)
{
base.Add(key.StringValue, value);
}
public void Remove(TKey key)
{
base.Remove(key.StringValue);
}
public TValue this[TKey index]
{
get => base[index.StringValue];
set => base[index.StringValue] = value;
}
public bool ContainsKey(TKey key)
{
return base.ContainsKey(key.StringValue);
}
}

C# JSON Serialization of Dictionary into {key:value, ...} instead of {key:key, value:value, ...}

Is it possible to serialize a .Net Dictionary<Key,Value> into JSON with DataContractJsonSerializer that is of the format:
{
key0:value0,
key1:value1,
...
}
I use Dictionary <K,V>, because there is not predefined structure of the inputs.
I'm interesting just for DataContractJsonSerializer result! I've already found a "Surrogate" example, but there is an additional "data" in the output, and if the dictionary <K, String> is, the escaping is false too.
I've found the solution, what a needed! First of all, a serializable "dictionary" class:
(Of course, this sample works just in one way, but I dont't need deserialization)
[Serializable]
public class MyJsonDictionary<K, V> : ISerializable {
Dictionary<K, V> dict = new Dictionary<K, V>();
public MyJsonDictionary() { }
protected MyJsonDictionary( SerializationInfo info, StreamingContext context ) {
throw new NotImplementedException();
}
public void GetObjectData( SerializationInfo info, StreamingContext context ) {
foreach( K key in dict.Keys ) {
info.AddValue( key.ToString(), dict[ key ] );
}
}
public void Add( K key, V value ) {
dict.Add( key, value );
}
public V this[ K index ] {
set { dict[ index ] = value; }
get { return dict[ index ]; }
}
}
Usage:
public class MainClass {
public static String Serialize( Object data ) {
var serializer = new DataContractJsonSerializer( data.GetType() );
var ms = new MemoryStream();
serializer.WriteObject( ms, data );
return Encoding.UTF8.GetString( ms.ToArray() );
}
public static void Main() {
MyJsonDictionary<String, Object> result = new MyJsonDictionary<String, Object>();
result["foo"] = "bar";
result["Name"] = "John Doe";
result["Age"] = 32;
MyJsonDictionary<String, Object> address = new MyJsonDictionary<String, Object>();
result["Address"] = address;
address["Street"] = "30 Rockefeller Plaza";
address["City"] = "New York City";
address["State"] = "NY";
Console.WriteLine( Serialize( result ) );
Console.ReadLine();
}
}
And the result:
{
"foo":"bar",
"Name":"John Doe",
"Age":32,
"Address":{
"__type":"MyJsonDictionaryOfstringanyType:#Json_Dictionary_Test",
"Street":"30 Rockefeller Plaza",
"City":"New York City",
"State":"NY"
}
}
Json.NET does this...
Dictionary<string, string> values = new Dictionary<string, string>();
values.Add("key1", "value1");
values.Add("key2", "value2");
string json = JsonConvert.SerializeObject(values);
// {
// "key1": "value1",
// "key2": "value2"
// }
More examples: Serializing Collections with Json.NET
In .NET 5 and later, you can simply write:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Dictionary<string, string> values = new();
values.Add("key1", "value1");
values.Add("key2", "value2");
string json = System.Text.Json.JsonSerializer.Serialize(values);
Console.WriteLine(json);
}
}
to get {"key1":"value1","key2":"value2"}.
No external dependency is needed.
use property UseSimpleDictionaryFormat on DataContractJsonSerializer and set it to true.
Does the job :)
I'm using out of the box MVC4 with this code (note the two parameters inside ToDictionary)
var result = new JsonResult()
{
Data = new
{
partials = GetPartials(data.Partials).ToDictionary(x => x.Key, y=> y.Value)
}
};
I get what's expected:
{"partials":{"cartSummary":"\u003cb\u003eCART SUMMARY\u003c/b\u003e"}}
Important: WebAPI in MVC4 uses JSON.NET serialization out of the box, but the standard web JsonResult action result doesn't. Therefore I recommend using a custom ActionResult to force JSON.NET serialization. You can also get nice formatting
Here's a simple actionresult JsonNetResult
http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx
You'll see the difference (and can make sure you're using the right one) when serializing a date:
Microsoft way:
{"wireTime":"\/Date(1355627201572)\/"}
JSON.NET way:
{"wireTime":"2012-12-15T19:07:03.5247384-08:00"}
Unfortunately, this is not currently possible in the latest version of DataContractJsonSerializer. See: http://connect.microsoft.com/VisualStudio/feedback/details/558686/datacontractjsonserializer-should-serialize-dictionary-k-v-as-a-json-associative-array
The current suggested workaround is to use the JavaScriptSerializer as Mark suggested above.
Good luck!
The MyJsonDictionary class worked well for me EXCEPT that the resultant output is XML encoded - so "0" becomes "0030".
I am currently stuck at .NET 3.5, as are many others, so many of the other solutions are not available to me.
"Turns the pictures" upside down and realized I could never convince Microsoft to give me the format I wanted but...
string json = XmlConvert.DecodeName(xmlencodedJson);
TADA!
The result is what you would expect to see - regular human readable and non-XML encoded.
Works in .NET 3.5.

Categories

Resources