JSON.Net Disable the Deserialization on Datetime

Json.NET Disable the deserialization on DateTime

When parsing from an object to JObject you can specify a JsonSerializer which instructs how to handle dates.

JObject.FromObject(new { time = s },
new JsonSerializer {
DateParseHandling = DateParseHandling.None
});

Unfortunately Parse doesn't have this option, although it would make sense to have it. Looking at the source for Parse we can see that all it does is instantiate a JsonReader and then passes that to Load. JsonReader does have parsing options.

You can achieve your desired result like this:

  using(JsonReader reader = new JsonTextReader(new StringReader(j1.ToString()))) {
reader.DateParseHandling = DateParseHandling.None;
JObject o = JObject.Load(reader);
}

For background, see Json.NET interprets and modifies ISO dates when deserializing to JObject #862, specifically this comment from JamesNK: I have no plans to change it, and I would do it again if give the chance.

Json.NET deserialization on DateTime - not the right time(-6 hour)

Serialization convert datetime to UTC. On deserialziation you need to convert it to local time:

try something like this:

var dateWithLocalTimeZone = myDate.ToLocalTime(); 

JSON.Net deserialization of DateTime values failing in some instances?

I've dug into it much further.

When JSON.Net serializes a DateTime instance, it calls TimeZone.CurrentTimeZone.GetUtcOffset(dt). When it reloads the DateTime, it assumes a UTC date and to convert to a local date calls utcDateTime.ToLocalTime() to convert to local time. It appears these two methods may not always use the same offset:

(Note: I'm in the Atlantic Time Zone.)

[Test, Sequential]
public void AnotherTest(
[Values(2006, 2006, 2006, 2006, 2006, 2006, 2006)] Int32 year,
[Values(10, 10, 10, 10, 11, 11, 11)] Int32 month,
[Values(26, 27, 30, 31, 1, 2, 3)] Int32 day)
{
var dt = new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Local);

var utcOffset1 = TimeZone.CurrentTimeZone.GetUtcOffset(dt);
var utcOffset2 = dt.Subtract(dt.ToUniversalTime());

Assert.AreEqual(utcOffset1, utcOffset2);
}

These pass on Oct 26 and Nov 3, and fail for the dates between. I also ran tests for various earlier years, with same results. For dates from 2007 through 2011, these all pass. For all failures I've found, utcOffset1 was -3 hours while utcOffset2 was -4 hours. (According to http://www.timeanddate.com/library/abbreviations/timezones/, Atlantic Standard Time is supposed to be UTC-4 and Atlantic Daylight Time should be UTC-3.) A few quick tests has shown me that there is also a problem at the start of daylight savings time as well, prior to 2007.

(I've opened an issue at https://connect.microsoft.com/VisualStudio/feedback/details/699491/timezone-getutcoffset-and-datetime-touniversaltime-not-consistent-for-brief-period-for-atlantic-time-zone.)

In the meantime, to fix this issue, the UTC offset just needs to be made consistent when serializing and deserializing the date, which means getting rid of the call to TimeZone.CurrentTimeZone.GetUtcOffset.

UPDATE

JamesNK updated JSON.Net on November 1 2011 so that time zone conversions use TimeZoneInfo instead of TimeZone, which appears to have fixed the problem.

UPDATE 2

Thanks @derekhat for the following additional information:

Got a few free minutes tonight. All tests passed for me using Windows 7 64-bit and compiled at command line with .NET 2.0 SDK (had to change var to explicit type declarations).

5 of the 7 tests failed with Visual Studio 2010 and .NET 4.

I then found the following documentation.

The GetUtcOffset method recognizes only the current daylight saving time
adjustment rule for the local time zone. As a result, it
is guaranteed to accurately return the UTC offset of a local time only
during the period in which the latest adjustment rule is in effect. It
may return inaccurate results if Time is a historic date and time
value that was subject to a previous adjustment rule.

The matter is further complicated by another piece of documentation that states: "On Windows XP systems, the ToUniversalTime method recognizes only the current adjustment rule when converting from local time to UTC." And a comment says the WinXP behaviour also exists on Windows Server 2003. The implication is that ToUniversalTime works properly with historic dates on newer versions of Windows, which seems to match your results.

ASP.NET WebApi 2: Disable deserialization of string argument containing ISO8601 Date

Solved this eventually by implementing a custom JsonConverter, which serializes string values along with a type hint (similar to Newtonsoft.Json's MetadataPropertyHandling setting in JsonSerializerSettings).

Here is the implementation which does the trick:

public class DateStringTypeConservingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(string));
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.ReadFrom(reader);
var typeMetadata = token["$type"];

if (typeMetadata?.Value<string>() == typeof(string).FullName)
{
return token.Value<string>("$value");
}

// Default behavior
return serializer.Deserialize(reader, objectType);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is string)
{
WriteJsonStringWithType(writer, value as string);
}
else
{
// Default behavior
serializer.Serialize(writer, value);
}
}

// Write the given string to the given JSON writer with type info:
// { $type: "System.String", $value: "<string content>" }
private void WriteJsonStringWithType(JsonWriter writer, string value)
{
writer.WriteStartObject();

writer.WritePropertyName("$type");
writer.WriteValue(typeof(string).FullName);

writer.WritePropertyName("$value");
writer.WriteValue(value);

writer.WriteEndObject();
}
}

And a little usage example:

    static void Main(string[] args)
{
var dateString = new Wrapper
{
Value = "2017-01-08T21:24:48.114Z"
};

var converters = new JsonConverter[] { new DateStringTypeConservingConverter() };
var serializedDateString = JsonConvert.SerializeObject(dateString, new JsonSerializerSettings
{
Converters = converters
});

var deserializedDateString =
JsonConvert.DeserializeObject<Wrapper>(serializedDateString, converters);

// Output: System.String
Console.WriteLine($"The type of deserialized value is: { deserializedDateString.Value.GetType() }");

Console.ReadKey();
}

class Wrapper
{
public object Value { get; set; }
}


Related Topics



Leave a reply



Submit