From 96946d6323018bdc5af16c5a49960fd77ff27364 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 9 Apr 2020 12:13:27 -0400 Subject: [PATCH] refactor: Rewrote JsonItemWrapper serialization for JsonConvert friendliness --- CHANGELOG.md | 3 + .../Json/JsonItemWrapperTest.cs | 18 +-- ICD.Common.Utils/Json/JsonItemWrapper.cs | 106 +++++++++--------- 3 files changed, 66 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 771f1b4..7751c16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - TableBuilder supports multi-line content - Added eIcdFileMode for IO platform agnosticism +### Changed + - Rewrote JsonItemWrapper serialization for JsonConvert friendliness + ## [11.0.0] - 2020-03-20 ### Added - Added Not null tag for ICDUriBuilder Constructor that takes a URI as an argument. diff --git a/ICD.Common.Utils.Tests/Json/JsonItemWrapperTest.cs b/ICD.Common.Utils.Tests/Json/JsonItemWrapperTest.cs index b6c8866..8b3271a 100644 --- a/ICD.Common.Utils.Tests/Json/JsonItemWrapperTest.cs +++ b/ICD.Common.Utils.Tests/Json/JsonItemWrapperTest.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using ICD.Common.Utils.Json; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using NUnit.Framework; namespace ICD.Common.Utils.Tests.Json @@ -14,7 +14,7 @@ namespace ICD.Common.Utils.Tests.Json [TestCase(1, typeof(int))] public void ItemTypeTest(object item, Type expected) { - Assert.AreEqual(expected, new JsonItemWrapper(item).ItemType); + Assert.AreEqual(expected, new JsonItemWrapper(item).Type); } [TestCase("")] @@ -27,19 +27,21 @@ namespace ICD.Common.Utils.Tests.Json [Test] public void WriteTest() { - JsonItemWrapper wrapper = new JsonItemWrapper(new List {1, 2, 3}); - string json = JsonUtils.Serialize(wrapper.Write); + const string expected = @"{""t"":""System.Collections.Generic.List`1[[System.Int32]]"",""i"":[1,2,3]}"; - Assert.Inconclusive(); + JsonItemWrapper wrapper = new JsonItemWrapper(new List {1, 2, 3}); + string json = JsonConvert.SerializeObject(wrapper); + + Assert.AreEqual(expected, json); } [Test] public void ReadTest() { - const string json = "{\"t\":\"System.Collections.Generic.List`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]\",\"i\":\"[1,2,3]\"}"; + const string json = @"{""t"":""System.Collections.Generic.List`1[[System.Int32]]"",""i"":[1,2,3]}"; - JObject jObject = JObject.Parse(json); - List wrappedObject = JsonItemWrapper.ReadToObject(jObject) as List; + JsonItemWrapper wrapper = JsonConvert.DeserializeObject(json); + List wrappedObject = wrapper.Item as List; Assert.NotNull(wrappedObject); Assert.AreEqual(3, wrappedObject.Count); diff --git a/ICD.Common.Utils/Json/JsonItemWrapper.cs b/ICD.Common.Utils/Json/JsonItemWrapper.cs index de152e8..5dd9944 100644 --- a/ICD.Common.Utils/Json/JsonItemWrapper.cs +++ b/ICD.Common.Utils/Json/JsonItemWrapper.cs @@ -1,97 +1,97 @@ using System; -using System.Text.RegularExpressions; +using ICD.Common.Properties; using ICD.Common.Utils.Extensions; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace ICD.Common.Utils.Json { /// /// Simple wrapper for serialization of an object and its type. /// + [JsonConverter(typeof(JsonItemWrapperConverter))] public sealed class JsonItemWrapper { - private const string TYPE_TOKEN = "t"; - private const string ITEM_TOKEN = "i"; - - private readonly object m_Item; - /// /// Gets the Type of the item. Returns null if the item is null. /// - public Type ItemType { get { return m_Item == null ? null : m_Item.GetType(); } } + [CanBeNull] + public Type Type { get; set; } /// /// Gets the wrapped item. /// - public object Item { get { return m_Item; } } + [CanBeNull] + public object Item { get; set; } + + /// + /// Constructor. + /// + public JsonItemWrapper() + : this(null) + { + } /// /// Constructor. /// /// - public JsonItemWrapper(object item) + public JsonItemWrapper([CanBeNull] object item) { - m_Item = item; + Item = item; + Type = item == null ? null : item.GetType(); } + } + + public sealed class JsonItemWrapperConverter : AbstractGenericJsonConverter + { + private const string TYPE_TOKEN = "t"; + private const string ITEM_TOKEN = "i"; /// - /// Writes the JsonItemWrapper as a JObject. + /// Override to write properties to the writer. /// /// - public void Write(JsonWriter writer) + /// + /// + protected override void WriteProperties(JsonWriter writer, JsonItemWrapper value, JsonSerializer serializer) { - if (writer == null) - throw new ArgumentNullException("writer"); + base.WriteProperties(writer, value, serializer); - writer.WriteStartObject(); + if (value.Type != null) + writer.WriteProperty(TYPE_TOKEN, value.Type.GetMinimalName()); + + if (value.Item != null) { - writer.WritePropertyName(TYPE_TOKEN); - writer.WriteType(ItemType); - writer.WritePropertyName(ITEM_TOKEN); - writer.WriteValue(JsonConvert.SerializeObject(m_Item)); + serializer.Serialize(writer, value.Item); } - writer.WriteEndObject(); } /// - /// Reads the JToken back to the wrapped object. + /// Override to handle the current property value with the given name. /// - /// - /// - public static object ReadToObject(JToken token) + /// + /// + /// + /// + protected override void ReadProperty(string property, JsonReader reader, JsonItemWrapper instance, JsonSerializer serializer) { - if (token == null) - throw new ArgumentNullException("token"); - - string typeString = (string)token.SelectToken(TYPE_TOKEN); - if (string.IsNullOrEmpty(typeString)) - return null; - - string itemString = (string)token.SelectToken(ITEM_TOKEN); - Type type = Type.GetType(typeString); - - if (type == null) + switch (property) { - typeString = typeString.Replace("_SimplSharp", "").Replace("_NetStandard", ""); - type = Type.GetType(typeString); + case TYPE_TOKEN: + instance.Type = reader.TokenType == JsonToken.Null ? null : reader.GetValueAsType(); + break; + + case ITEM_TOKEN: + if (instance.Type == null && reader.TokenType != JsonToken.Null) + throw new FormatException("No Type for associated Item"); + instance.Item = serializer.Deserialize(reader, instance.Type); + break; + + default: + base.ReadProperty(property, reader, instance, serializer); + break; } - - if (type == null) - { - typeString = AddSimplSharpSuffix(typeString); - type = Type.GetType(typeString); - } - - return JsonConvert.DeserializeObject(itemString, type); - } - - private static string AddSimplSharpSuffix(string typeString) - { - return Regex.Replace(typeString, - "(?'prefix'[^,]+, )(?'assembly'[^,]*)(?'suffix', .*)", - "${prefix}${assembly}_SimplSharp${suffix}"); } } }