Skip to content

Instantly share code, notes, and snippets.

@ErgEnn
Last active January 27, 2021 19:52
Show Gist options
  • Save ErgEnn/23dff038798941df683231fd43016c9d to your computer and use it in GitHub Desktop.
Save ErgEnn/23dff038798941df683231fd43016c9d to your computer and use it in GitHub Desktop.
Primitive wrapper for Newtonsoft.Json library. Object Calisthenics suggests, that primitive types should be avoided in code. This JsonConverter allows every primitive to be wrapped in object and unwrapped back into primitive JSON tokens with ease.
public class PrimitiveConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var token = JToken.FromObject((value as PrimitiveWrapper)._value);
token.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var primitiveType = objectType.BaseType.GenericTypeArguments[0];
var constructorInfo = objectType.GetConstructor(new Type[] {primitiveType});
var ctorParam = Expression.Parameter(primitiveType, constructorInfo.GetParameters()[0].Name);
var ctorExpression = Expression.Lambda(Expression.New(constructorInfo, ctorParam), ctorParam).Compile();
var jToken = JToken.ReadFrom(reader);
var val = jToken.ToObject(primitiveType);
return val is null ? null : ctorExpression.DynamicInvoke(val);
}
public override bool CanConvert(Type objectType)
{
return objectType.BaseType.BaseType == typeof(PrimitiveWrapper);
}
}
public abstract class PrimitiveWrapper<T> : PrimitiveWrapper
{
public PrimitiveWrapper(T value)
{
_value = value ?? throw new ArgumentNullException(nameof(value));
}
}
public abstract class PrimitiveWrapper
{
internal object _value;
}
//---------------------------- USAGE EXAMPLE ----------------------------
public class TestClass
{
public TestClass(FirstName firstName, FirstName previousFirstName, Age age, string city)
{
FirstName = firstName ?? throw new ArgumentNullException(nameof(firstName));
PreviousFirstName = previousFirstName ?? throw new ArgumentNullException(nameof(previousFirstName));
Age = age ?? throw new ArgumentNullException(nameof(age));
City = city ?? throw new ArgumentNullException(nameof(city));
}
public FirstName FirstName { get; }
public FirstName PreviousFirstName { get; }
public Age Age { get; }
public string City { get; }
}
public class Age : PrimitiveWrapper<int>
{
public Age(int value) : base(value)
{
}
}
public class FirstName : PrimitiveWrapper<string>
{
public FirstName(string value) : base(value)
{
}
}
//------------------------Tests require FluentAssertion --------------------------
[Test]
public void GivenJsonWithPrimitives_WhenDeserialized_ThenJsonTokenValuesAreWrappedInWrapperObjects()
{
string json = "{\"FirstName\":\"Ergo\",\"PreviousFirstName\":\"Eku\",\"Age\":23,\"City\":\"Tallinn\"}";
var settings = new JsonSerializerSettings() { Converters = new List<JsonConverter>() { new PrimitiveConverter() } };
var obj = JsonConvert.DeserializeObject<TestClass>(json,settings);
obj.FirstName._value.Should().Be("Ergo");
obj.PreviousFirstName._value.Should().Be("Eku");
obj.Age._value.Should().Be(23);
obj.City.Should().Be("Tallinn");
}
[Test]
public void GivenObject_WhenSerialized_ThenOutputJsonShouldContainPrimitives()
{
var obj = new TestClass(new FirstName("Ergo"),new FirstName("Eku"), new Age(23), "Tallinn");
var settings = new JsonSerializerSettings() {Converters = new List<JsonConverter>(){new PrimitiveConverter()}};
string json = JsonConvert.SerializeObject(obj,settings);
json.Should().Be("{\"FirstName\":\"Ergo\",\"PreviousFirstName\":\"Eku\",\"Age\":23,\"City\":\"Tallinn\"}");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment