I'm trying to use JSON.Net to serialize to BSON, but the original offset does not appear to be respected.
Can you see a problem how I'm trying to make this work?
[Test]
public void SerializeDateTimeOffsetToBson()
{
var serializer = new Newtonsoft.Json.JsonSerializer {
TypeNameHandling = TypeNameHandling.Auto,
DateParseHandling = DateParseHandling.DateTimeOffset,
DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind
};
var negOffset = new DateTimeOffset(2014, 7, 10, 0, 0, 0, new TimeSpan(-5, 0, 0));
var gmtOffset = new DateTimeOffset(2014, 7, 10, 0, 0, 0, new TimeSpan());
var posOffset = new DateTimeOffset(2014, 7, 10, 0, 0, 0, new TimeSpan(5, 0, 0));
var dt = new {
negOffset = negOffset,
gmtOffset = gmtOffset,
posOffset = posOffset
};
byte[] serialized;
using (var ms = new MemoryStream())
using (var writer = new BsonWriter(ms)) {
serializer.Serialize(writer, dt);
writer.Close();
serialized = ms.ToArray();
}
dynamic deserializedDt;
using (var ms = new MemoryStream(serialized))
using (var rdr = new BsonReader(ms)) {
deserializedDt = (dynamic)serializer.Deserialize(rdr);
rdr.Close();
}
Assert.IsTrue(deserializedDt.negOffset == dt.negOffset);
Assert.IsTrue(deserializedDt.posOffset == dt.posOffset);
Assert.IsTrue(deserializedDt.gmtOffset == dt.gmtOffset);
}
All three of the assertions will fail.
After deserialization, deserializedDt.negOffset is July 9th 2014 10 PM with an offset of -07:00 (computer's current time zone), deserializedDt.posOffset is July 9th 2014 12 PM with an offset of -07:00, and deserializedDt.gmtOffset is July 9th 2014 5 PM with an offset of -07:00.
Using JSON.Net 8.0.3 in a .Net 4.0 project.
UPDATE------------------
After further investigation, I've opened an issue about this on Github at https://github.com/JamesNK/Newtonsoft.Json/issues/898
The BSON spec doesn't allow for storing a DateTime's offset; it stores UTC datetime as an Int64, milliseconds since the Unix epoch.
If you don't want to lose the offset, you can create a JsonConverter that will separate the DateTime from the Offset to serialize (and deserialize) both separately. For example:
public class DateTimeOffsetConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(DateTimeOffset) == objectType;
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
Newtonsoft.Json.JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
return null;
reader.Read(); // PropertyName "DateTimeInTicks"
reader.Read(); // Property value
var ticks = (Int64)reader.Value;
reader.Read(); // PropertyName "Offset"
reader.Read(); // Property value
var offset = TimeSpan.Parse((String)reader.Value);
// Move forward to JsonToken.EndObject
reader.Read();
return new DateTimeOffset(ticks, offset);
}
public override void WriteJson(
JsonWriter writer,
object value,
Newtonsoft.Json.JsonSerializer serializer)
{
var dateTimeOffset = (DateTimeOffset)value;
var toSerialize = new {
DateTimeInTicks = dateTimeOffset.DateTime.Ticks,
Offset = dateTimeOffset.Offset
};
serializer.Serialize(writer, toSerialize);
}
}
You can then apply it to your classes as follows:
public class TestClass
{
public Int32 TestInt { get; set; }
[JsonConverter(typeof(DateTimeOffsetConverter))]
public DateTimeOffset TestDateTimeOffset { get; set; }
public String TestString { get; set; }
[JsonConverter(typeof(DateTimeOffsetConverter))]
public DateTimeOffset? TestNullableDateTimeOffset { get; set; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With