Serialize dictionary as array (of key value pairs)
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",
}
},
]
Json.net deserialize dictionary and assign dictionary key to dictionary value's member variable
Immediately after typing the question, I figure out that the Json.net supports a few serialization callback attributes (from System.Runtime.Serialization
), which is exactly what I need.
[OnDeserialized]
internal void OnDeserializedMethod(StreamingContext context)
{
Member4 = "This value was set after deserialization.";
}
Alternatively, instead of using a Dictionary, there's also KeyedCollection<TKey,TItem>, which reduces the need for duplicated dictionary key. The downside is that it won't deserialize and serialize to the desired json format I want by default, and require some customization.
How do you make a tuple or keyvalue pair json serialize like a dictionary in C#
One way to solve this problem is via the usage of ILookup<,>
and a custom JsonConverter
.
- You can think of the
ILookup<T1, T2>
as aDictionary<T1, IEnumerable<T2>>
- So, it is a Bag data structure.
var dataSource = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("test", "value1"),
new KeyValuePair<string, string>("test", "value2"),
new KeyValuePair<string, string>("test", "value3"),
new KeyValuePair<string, string>("test2", "somevalue"),
};
var toBeSerializedData = dataSource.ToLookup(pair => pair.Key, pair => pair.Value);
var serializedData =JsonConvert.SerializeObject(toBeSerializedData);
This will generate the following json:
[
[
"value1",
"value2",
"value3"
],
[
"somevalue"
]
]
- As you see the values are grouped by the
Key
s. - But the keys are omitted and the values are in arrays.
In order to overcome of these we can define a custom JsonConverter
:
public class LookupSerializer : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType.GetInterfaces()
.Any(a => a.IsGenericType && a.GetGenericTypeDefinition() == typeof(ILookup<,>));
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
writer.WriteStartObject();
foreach (object values in (IEnumerable)value)
{
var keyProp = values.GetType().GetProperty("Key");
var keyValue = keyProp.GetValue(values, null);
foreach (var val in (IEnumerable)values)
{
writer.WritePropertyName(keyValue.ToString());
writer.WriteValue(val.ToString());
}
}
writer.WriteEndObject();
}
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
=> throw new NotImplementedException();
}
CanConvert
: restricts the usage only forILookup
.WriteJson
: iterates through the groups with the outerforeach
and iterates through the values via the innerforeach
.- Here we can't use
JObject
(and itsAdd
method) to create the output because it would fail with anArgumentException
:
Can not add property test to Newtonsoft.Json.Linq.JObject.
Property with the same name already exists on object.
- So, we have to use lower level
JsonWriter
to construct the output.
If you pass an instance of the LookupSerialzer
to the SerializeObject
(or register the converter application-wide):
var serializedData =JsonConvert.SerializeObject(toBeSerializedData, new LookupSerializer());
then the output will be the one as desired:
{
"test" : "value1",
"test" : "value2",
"test" : "value3",
"test2" : "somevalue"
}
Related Topics
Merge Multiple Lists into One List With Linq
How to Asynchronously Wait for X Seconds and Execute Something Then
How to Handle [Null] Values of Datetime from SQL Server to a Datetimepicker
Using Formflow Dialog in Bot Framework
File and Line Numbers for Errors Are Not Displaying in the Error List Unless the File Is Open
How to Compare Key/Value Dictionary With == Operator on a Ireadonlycollection<String>
A Better Way to Use Automapper to Flatten Nested Objects
C# - How to Save Byte Values to File With Smallest Size Possible
Check If Database Exists Before Creating
Fastest Way to Find Strings in a File
How to Round Up the Time to the Nearest X Minutes
Posting Jsonobject With Httpclient from Web API
Newtonsoft Json Dynamic Property Name
Console.Writeline Does Not Show Up in Output Window
How to Properly Make a Http Web Get Request
How to Check If a Windows Form Is Already Open, and Close It If It Is