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
Is There a Generic Constructor with Parameter Constraint in C#
Solution for Overloaded Operator Constraint in .Net Generics
What's the Point of the Var Keyword
Best Way to Access a Control on Another Form in Windows Forms
Replace "\\" with "\" in a String in C#
How to Convert a Gi-Normous Integer (In String Format) to Hex Format? (C#)
Using of Inotifypropertychanged
Performance Surprise with "As" and Nullable Types
How to Use Openfiledialog to Select a Folder
Omitting All Xsi and Xsd Namespaces When Serializing an Object in .Net
How to Check the Number of Bytes Consumed by a Structure
Calculating Distance Between Two Latitude and Longitude Geocoordinates
How to Get the Calling Method Name and Type Using Reflection