Skip to content

Instantly share code, notes, and snippets.

@borland
Last active April 29, 2018 11:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save borland/102a44fd14f7cdf2376c22db05d15cce to your computer and use it in GitHub Desktop.
Save borland/102a44fd14f7cdf2376c22db05d15cce to your computer and use it in GitHub Desktop.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
public class Program
{
static readonly string jsonExample = @"
{
""intValue"": 1,
""stringValue"": ""a string"",
""looksLikeInt"": ""12"",
""looksLikeDate"": ""2018-04-29T22:20:01Z"",
""shouldBeDate"": ""2018-04-29T22:20:01Z""
}
";
class RawObject
{
public int IntValue { get; set; }
public string StringValue { get; set; }
public string LooksLikeInt { get; set; }
public string LooksLikeDate { get; set; }
public string ShouldBeDate { get; set; }
public override string ToString() => $"[RawObject IntValue={IntValue} StringValue=\"{StringValue}\" LooksLikeInt=\"{LooksLikeInt}\" LooksLikeDate=\"{LooksLikeDate}\" ShouldBeDate=\"{ShouldBeDate}\"]";
}
class CoerceObject
{
public int IntValue { get; set; }
public string StringValue { get; set; }
public int LooksLikeInt { get; set; }
public DateTime LooksLikeDate { get; set; }
public DateTime ShouldBeDate { get; set; }
public override string ToString() => $"[CoerceObject IntValue={IntValue} StringValue=\"{StringValue}\" LooksLikeInt={LooksLikeInt} LooksLikeDate={LooksLikeDate} ShouldBeDate={ShouldBeDate}]";
}
class ExtensionObject
{
public int IntValue { get; set; }
public string StringValue { get; set; }
public DateTime ShouldBeDate { get; set; }
[JsonExtensionData]
public Dictionary<string, JToken> Other { get; set; }
}
[JsonConverter(typeof(ExtensionObjectNoDateTimeGuessing.Converter))]
class ExtensionObjectNoDateTimeGuessing
{
public int IntValue { get; set; }
public string StringValue { get; set; }
public DateTime ShouldBeDate { get; set; }
[JsonExtensionData]
public JObject Other { get; set; }
class Converter : JsonConverter
{
public override bool CanConvert(Type objectType) => throw new NotImplementedException();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var old = reader.DateParseHandling;
try {
reader.DateParseHandling = DateParseHandling.None;
existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
serializer.Populate(reader, existingValue);
return existingValue;
}
finally {
reader.DateParseHandling = old;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
}
public static void Main(string[] args)
{
// GOOD: Json de-serialises correctly to whatever type you tell it to
var rawObject = JsonConvert.DeserializeObject<RawObject>(jsonExample);
Console.WriteLine(rawObject);
// GOOD: Json de-serialises correctly to whatever type you tell it to.
// It will coerce strings to ints and DateTimes, but we've told it to do that so fair enough
var coerceObject = JsonConvert.DeserializeObject<CoerceObject>(jsonExample);
Console.WriteLine(coerceObject);
// OK: Everything's a string, but then we have to do the DateTime.parse ourselves which is no fun
var stringStringDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonExample);
Console.WriteLine(stringStringDict);
// BAD: Json.Net guesses that our string looks like a DateTime and mishandles it
var stringObjDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonExample);
Console.WriteLine(stringObjDict);
// BAD: Asking for JObject or JToken is no better than Dictionary<string, object> Json.Net guesses that our string looks like a DateTime and mishandles it
// BEFORE we get a chance to deal with it
var extObj = JsonConvert.DeserializeObject<ExtensionObject>(jsonExample);
Console.WriteLine(extObj);
// BAD: If the root is JObject, Json.NET still guesses before it gives it to us
var jobject = JsonConvert.DeserializeObject<JObject>(jsonExample);
var joDate = jobject["looksLikeDate"].Value<DateTime>();
var joString = jobject["looksLikeDate"].Value<string>();
Console.WriteLine(jobject);
// GOOD: Explicit serializer settings telling it not to guess. If we ask for a DateTime it will kindly give us one though
var jobject2 = JsonConvert.DeserializeObject<JObject>(jsonExample, new JsonSerializerSettings { DateParseHandling = DateParseHandling.None });
var joDate2 = jobject2["looksLikeDate"].Value<DateTime>();
var joString2 = jobject2["looksLikeDate"].Value<string>();
Console.WriteLine(jobject2);
// BEST: Custom JsonConverter switches off DateTime guessing, however if it's using reflection to match against an explict C# DateTime field it does the conversion
var extObj2 = JsonConvert.DeserializeObject<ExtensionObjectNoDateTimeGuessing>(jsonExample);
var extObj2Date = extObj2.Other["looksLikeDate"].Value<DateTime>();
var extObj2String = extObj2.Other["looksLikeDate"].Value<string>();
Console.WriteLine(extObj2);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment