Json.net 将LocalDate从版本1反序列化为版本2

Json.net 将LocalDate从版本1反序列化为版本2,json.net,nodatime,Json.net,Nodatime,不幸的是,当我们开始这个项目时,我们没有将配置添加到布线中 这意味着我们在NEventStore中有这样的JSON文档 注意。简化视图。实际上不是事件表示 dates.json { "$type": "NodatimeIssueTest.Product, NodatimeIssueTest", "FirstDate": { "$type": "NodaTime.LocalDate, NodaTime", "ticks": 1230422400000

不幸的是,当我们开始这个项目时,我们没有将配置添加到布线中

这意味着我们在NEventStore中有这样的JSON文档

注意。简化视图。实际上不是事件表示

dates.json

{
    "$type": "NodatimeIssueTest.Product, NodatimeIssueTest",
    "FirstDate": {
        "$type": "NodaTime.LocalDate, NodaTime",
        "ticks": 12304224000000000,
        "calendar": "ISO"
    },
    "SecondDate": {
        "$type": "System.Nullable`1[[NodaTime.LocalDate, NodaTime]], mscorlib",
        "ticks": 12304224000000000,
        "calendar": "ISO"
    },
    "OtherDates": [
        {
            "$type": "NodaTime.LocalDate, NodaTime",
            "ticks": 12304224000000000,
            "calendar": "ISO"
        }
    ],
    "FirstDateTime": {
        "$type": "NodaTime.LocalDateTime, NodaTime",
        "ticks": 12304734100000000,
        "calendar": "ISO"
    },
    "FirstInstant": {
        "$type": "NodaTime.Instant, NodaTime",
        "ticks": 12304734100000000
    }
}
而不是这个

{
    "$type": "NodatimeIssueTest.Product, NodatimeIssueTest",
    "FirstDate": "2008-12-28",
    "SecondDate": "2008-12-28",
    "OtherDates": [
        "2008-12-28"
    ],
    "FirstDateTime": "2008-12-28T14:10:10",
    "FirstInstant": "2008-12-28T14:10:10Z"
}
因此,我们很难升级到最新的NodaTime包,因为我们无法再反序列化json文档

一种解决方案是读取所有NEventStore提交,并使用正确的节点时间解析器进行序列化。但如果这可以避免,我会很高兴

另一种选择是进行自定义转换和数据绑定


但这需要低水平的节点时间逻辑。尽管我们不需要涵盖所有日历,例如。

带有自定义转换器和日期活页夹的解决方案

using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using NodaTime;
using NodaTime.Serialization.JsonNet;
using NUnit.Framework;

namespace NodatimeIssueTest
{
    [TestFixture]
    public class TestClass
    {
        [Test]
        public void Deserialize()
        {
            var serializer = new JsonSerializer();
            serializer.NullValueHandling = NullValueHandling.Ignore;
            serializer.TypeNameHandling = TypeNameHandling.Objects;

            //Custom converters and binder
            serializer.Converters.Add(new NodaLocalDateConverter());
            serializer.Converters.Add(new NodaLocalDateTimeConverter());
            serializer.Converters.Add(new NodaInstantConverter());
            serializer.SerializationBinder = new CustomBinder();

            using (var sr = new StreamReader($@"{TestContext.CurrentContext.TestDirectory}\dates.json"))
            using (var reader = new JsonTextReader(sr))
            {
                var product = serializer.Deserialize<Product>(reader);
                Assert.AreEqual(new LocalDate(2008, 12, 28), product.FirstDate);
                Assert.AreEqual(new LocalDate(2008, 12, 28), product.SecondDate);
                Assert.AreEqual(new LocalDate(2008, 12, 28), product.OtherDates[0]);
                Assert.AreEqual(new LocalDateTime(2008, 12, 28, 14, 10, 10), product.FirstDateTime);
                Assert.AreEqual(Instant.FromUtc(2008, 12, 28, 14, 10, 10), product.FirstInstant);
            }
        }
    }

    public class Product
    {
        public LocalDate FirstDate { get; set; }
        public LocalDate? SecondDate { get; set; }
        public List<LocalDate> OtherDates { get; set; }

        public LocalDateTime FirstDateTime { get; set; }
        public Instant FirstInstant { get; set; }
    }

    public class NodaLocalDateConverter : JsonConverter
    {
        public override bool CanWrite => false;
        public override bool CanRead => true;

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            if (reader.TokenType == JsonToken.StartObject)
            {
                var custom = (CustomLocalDate) serializer.Deserialize(reader, typeof(CustomLocalDate));
                var dateTime = new DateTime(custom.Ticks, DateTimeKind.Utc).AddTicks(new DateTime(1970, 1, 1).Ticks);
                var local = LocalDate.FromDateTime(dateTime, CalendarSystem.Iso);

                return local;
            }

            return null;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(LocalDate) || objectType == typeof(LocalDate?);
        }
    }

    public class NodaLocalDateTimeConverter : JsonConverter
    {
        public override bool CanWrite => false;
        public override bool CanRead => true;

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            if (reader.TokenType == JsonToken.StartObject)
            {
                var custom = (CustomLocalDateTime) serializer.Deserialize(reader, typeof(CustomLocalDateTime));
                var dateTime = new DateTime(custom.Ticks, DateTimeKind.Utc).AddTicks(new DateTime(1970, 1, 1).Ticks);
                var local = LocalDateTime.FromDateTime(dateTime, CalendarSystem.Iso);

                return local;
            }

            return null;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(LocalDateTime) || objectType == typeof(LocalDateTime?);
        }
    }

    public class NodaInstantConverter : JsonConverter
    {
        public override bool CanWrite => false;
        public override bool CanRead => true;

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            if (reader.TokenType == JsonToken.StartObject)
            {
                var custom = (CustomInstant) serializer.Deserialize(reader, typeof(CustomInstant));
                var dateTime = new DateTime(custom.Ticks, DateTimeKind.Utc).AddTicks(new DateTime(1970, 1, 1).Ticks);
                var local = Instant.FromDateTimeUtc(dateTime);

                return local;
            }

            return null;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(Instant) || objectType == typeof(Instant?);
        }
    }

    public class CustomBinder : DefaultSerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            switch (typeName)
            {
                case "NodaTime.LocalDate": return typeof(CustomLocalDate);
                case "System.Nullable`1[[NodaTime.LocalDate, NodaTime]]": return typeof(CustomLocalDate);

                case "NodaTime.LocalDateTime": return typeof(CustomLocalDateTime);
                case "System.Nullable`1[[NodaTime.LocalDateTime, NodaTime]]": return typeof(CustomLocalDateTime);

                case "NodaTime.Instant": return typeof(CustomInstant);
                case "System.Nullable`1[[NodaTime.Instant, NodaTime]]": return typeof(CustomInstant);

                default: return base.BindToType(assemblyName, typeName);
            }
        }
    }

    public class CustomLocalDate
    {
        [JsonProperty("ticks")] public long Ticks { get; set; }

        [JsonProperty("calendar")] public string Calendar { get; set; }
    }

    public class CustomLocalDateTime
    {
        [JsonProperty("ticks")] public long Ticks { get; set; }

        [JsonProperty("calendar")] public string Calendar { get; set; }
    }

    public class CustomInstant
    {
        [JsonProperty("ticks")] public long Ticks { get; set; }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.IO;
使用Newtonsoft.Json;
使用Newtonsoft.Json.Serialization;
使用NodaTime;
使用NodaTime.Serialization.JsonNet;
使用NUnit.Framework;
命名空间节点测试
{
[测试夹具]
公共类TestClass
{
[测试]
public void反序列化()
{
var serializer=new JsonSerializer();
serializer.NullValueHandling=NullValueHandling.Ignore;
serializer.TypeNameHandling=TypeNameHandling.Objects;
//定制转换器和活页夹
serializer.Converters.Add(新的NodaLocalDateConverter());
serializer.Converters.Add(新的NodaLocalDateTimeConverter());
serializer.Converters.Add(new NodaInstantConverter());
serializer.SerializationBinder=新的CustomBinder();
使用(var sr=newstreamreader($@“{TestContext.CurrentContext.TestDirectory}\dates.json”))
使用(var reader=newjsontextreader(sr))
{
var product=serializer.Deserialize(读取器);
AreEqual(新的LocalDate(2008,12,28),product.FirstDate);
AreEqual(新的LocalDate(2008,12,28),product.SecondDate);
Assert.AreEqual(新的LocalDate(2008,12,28),product.OtherDates[0]);
AreEqual(新的LocalDateTime(2008,12,28,14,10,10),product.FirstDateTime);
Assert.AreEqual(Instant.FromUtc(2008,12,28,14,10,10),product.FirstInstant);
}
}
}
公共类产品
{
public LocalDate FirstDate{get;set;}
public LocalDate?SecondDate{get;set;}
公共列表其他日期{get;set;}
public LocalDateTime FirstDateTime{get;set;}
公共即时FirstInstant{get;set;}
}
公共类NodeLocalDateConverter:JsonConverter
{
公共覆盖boolcanwrite=>false;
public override bool CanRead=>true;
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
抛出新的NotImplementedException();
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
if(reader.TokenType==JsonToken.Null)返回Null;
if(reader.TokenType==JsonToken.StartObject)
{
var custom=(CustomLocalDate)序列化程序。反序列化(读取器,typeof(CustomLocalDate));
var dateTime=new dateTime(custom.Ticks,DateTimeKind.Utc).AddTicks(new dateTime(1970,1,1).Ticks);
var local=LocalDate.FromDateTime(dateTime,CalendarSystem.Iso);
返回本地;
}
返回null;
}
公共覆盖布尔CanConvert(类型objectType)
{
返回objectType==typeof(LocalDate)| objectType==typeof(LocalDate?);
}
}
公共类NodaLocalDateTimeConverter:JsonConverter
{
公共覆盖boolcanwrite=>false;
public override bool CanRead=>true;
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
抛出新的NotImplementedException();
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
if(reader.TokenType==JsonToken.Null)返回Null;
if(reader.TokenType==JsonToken.StartObject)
{
var custom=(CustomLocalDateTime)序列化程序。反序列化(读取器,typeof(CustomLocalDateTime));
var dateTime=new dateTime(custom.Ticks,DateTimeKind.Utc).AddTicks(new dateTime(1970,1,1).Ticks);
var local=LocalDateTime.FromDateTime(dateTime,CalendarSystem.Iso);
返回本地;
}
返回null;
}
公共覆盖布尔CanConvert(类型objectType)
{
返回objectType==typeof(LocalDateTime)| | objectType==typeof(LocalDateTime?);
}
}
公共类NodeInstantConverter:JsonConverter
{
公共覆盖boolcanwrite=>false;
public override bool CanRead=>true;
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
抛出新的NotImplementedException();
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
if(reader.TokenType==JsonToken.Null)返回Null;
if(reader.TokenType==JsonToken.StartObject)
{
var custom=(CustomInstant)序列化程序。反序列化(reader,typeof(CustomInstant));
var dateTime=new dateTime(custom.Ticks,DateTimeKind.Utc).AddTicks(new dateTime(1970,1,1).Ticks);
var local=Instant.FromDateTimeUtc(日期时间);
返回本地;