Created
March 5, 2020 11:09
-
-
Save Arakade/d05575965a16bef96d67cf82335831e8 to your computer and use it in GitHub Desktop.
Custom JSON.Net Dictionary serialization for when value object contains the key the dictionary uses to map to it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See https://unreasonablygoodsoftware.wordpress.com/2020/03/05/json-net-custom-dictionary-serialization/ | |
using System; | |
using System.Collections.Generic; | |
using JetBrains.Annotations; | |
using Newtonsoft.Json; | |
using NUnit.Framework; | |
using UnityEngine.Scripting; | |
namespace packageNameRemoved { | |
public class CustomJsonSerializationTestSimple { | |
[JsonObject(MemberSerialization.OptIn)] | |
private class HasCustomDict { | |
[JsonProperty] | |
public Dictionary<string, ClassHasString> dict; | |
public HasCustomDict(IList<ClassHasString> list) { | |
dict = new Dictionary<string, ClassHasString>(); | |
foreach (var classHasString in list) { | |
dict[classHasString.id] = classHasString; | |
} | |
} | |
[JsonConstructor] | |
public HasCustomDict(Dictionary<string, ClassHasString> dict) { | |
this.dict = dict; | |
} | |
public override string ToString() { | |
return $"{GetType().Name}({nameof(dict)}:[\n {(null != dict ? string.Join("\n ", dict) : "null")}])"; | |
} | |
} | |
[JsonObject(MemberSerialization.OptIn)] | |
private class ClassHasString { | |
[JsonProperty] | |
public string id; | |
[JsonProperty] | |
public int num; | |
[JsonConstructor] | |
public ClassHasString(string id, int num) { | |
this.id = id; | |
this.num = num; | |
} | |
public override string ToString() { | |
return $"({nameof(id)}:{id}, {nameof(num)}:{num})"; | |
} | |
} | |
[Test] | |
public void simpleSerializationWorks() { | |
var hasCustomDict = new HasCustomDict(new []{ | |
new ClassHasString("one", 1), | |
new ClassHasString("two", 2), | |
new ClassHasString("three", 3), | |
}); | |
var beforeStr = hasCustomDict.ToString(); | |
log("before: {0}", beforeStr); | |
var json = JsonConvert.SerializeObject(hasCustomDict); | |
log("JSON: {0}", json); | |
var deserialized = JsonConvert.DeserializeObject<HasCustomDict>(json); | |
var afterStr = deserialized.ToString(); | |
log("After: {0}", afterStr); | |
Assert.AreEqual(beforeStr, afterStr); | |
} | |
[StringFormatMethod("formatStr")] | |
private static void log([NotNull] string formatStr, params object[] args) { | |
Console.Out.WriteLine(formatStr, args); | |
} | |
} | |
public class CustomJsonSerializationWithListOfValues { | |
[JsonObject(MemberSerialization.OptIn), Preserve] | |
private class HasCustomDict { | |
[JsonProperty] | |
public Dictionary<string, ClassHasString> dict; | |
public HasCustomDict(IList<ClassHasString> list) { | |
dict = new Dictionary<string, ClassHasString>(); | |
foreach (var classHasString in list) { | |
dict[classHasString.id] = classHasString; | |
} | |
} | |
[JsonConstructor] | |
public HasCustomDict(Dictionary<string, ClassHasString> dict) { | |
this.dict = dict; | |
} | |
public override string ToString() { | |
return $"{GetType().Name}({nameof(dict)}:[\n {(null != dict ? string.Join("\n ", dict) : "null")}])"; | |
} | |
} | |
[JsonObject(MemberSerialization.OptIn), Preserve] | |
private class ClassHasString { | |
[JsonProperty] | |
public string id; | |
[JsonProperty] | |
public int num; | |
[JsonConstructor] | |
public ClassHasString(string id, int num) { | |
this.id = id; | |
this.num = num; | |
} | |
public override string ToString() { | |
return $"({nameof(id)}:{id}, {nameof(num)}:{num})"; | |
} | |
} | |
private class CustomDictFormatterAsListOfValues<TKey, TValue> : JsonConverter { | |
public CustomDictFormatterAsListOfValues(Func<TValue, TKey> getKeyFromValue) { | |
this.getKeyFromValue = getKeyFromValue; | |
} | |
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { | |
log("writer:{0}, value:{1} (type:{2}), serializer:{3}\n", writer, value, value.GetType(), serializer); | |
writer.WriteStartArray(); | |
foreach (var kvp in (IDictionary<TKey, TValue>) value) { | |
serializer.Serialize(writer, kvp.Value); | |
} | |
writer.WriteEndArray(); | |
} | |
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { | |
log("reader:{0}, objectType:{1}, existingValue:{2}, serializer:{3}\n", reader, objectType, existingValue, serializer); | |
var list = serializer.Deserialize<IList<TValue>>(reader); | |
var dict = new Dictionary<TKey, TValue>(); | |
foreach (var t in list) { | |
dict[getKeyFromValue(t)] = t; | |
} | |
return dict; | |
} | |
public override bool CanWrite => true; | |
public override bool CanRead => true; | |
public override bool CanConvert(Type objectType) { | |
var canConvert = typeof(IDictionary<TKey, TValue>).IsAssignableFrom(objectType); | |
log("convert? {0} = {1}{2}", objectType, canConvert, canConvert ? "\n" : ""); | |
return canConvert; | |
} | |
private readonly Func<TValue, TKey> getKeyFromValue; | |
} | |
[Test] | |
public void customSerializationWorksWithListOfValues() { | |
var hasCustomDict = new HasCustomDict(new []{ | |
new ClassHasString("one", 1), | |
new ClassHasString("two", 2), | |
new ClassHasString("three", 3), | |
}); | |
var beforeStr = hasCustomDict.ToString(); | |
log("before: {0}\n", beforeStr); | |
var customDictFormatterAsListOfValues = new CustomDictFormatterAsListOfValues<string, ClassHasString>(c => c.id); | |
var json = JsonConvert.SerializeObject(hasCustomDict, customDictFormatterAsListOfValues); | |
log("\nJSON: {0}\n", json); | |
var deserialized = JsonConvert.DeserializeObject<HasCustomDict>(json, customDictFormatterAsListOfValues); | |
var afterStr = deserialized.ToString(); | |
log("\nAfter: {0}\n", afterStr); | |
Assert.AreEqual(beforeStr, afterStr); | |
} | |
[StringFormatMethod("formatStr")] | |
private static void log([NotNull] string formatStr, params object[] args) { | |
Console.Out.WriteLine(formatStr, args); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment