如何在同一个 JSON 对象中反序列化不同的 NodaTime LocalDate 模式

How do I deserialise different NodaTime LocalDate patterns within the same JSON object

我正在尝试使用 NodaTime 来解释从第三方 API 检索到的日期。在同一个响应中,日期以一系列烦人的格式出现,我特别遇到的问题类似于:

{
    "ShortDate": "2017-01-01",
    "LongDate": "01 January 2017"
}

我可以使用 NodaPatternConverter 正确反序列化一种格式或另一种格式,但不能同时反序列化两种格式。

一个显示问题的简单例子是这样的:

using Newtonsoft.Json;
using NodaTime;
using NodaTime.Serialization.JsonNet;
using NodaTime.Text;

namespace NodaLocalDateConverterTest
{
    class ExampleDatedModel
    {
        public LocalDate ShortDate { get; set; }

        public LocalDate LongDate { get; set; }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var exampleJsonString =
@"{
    ""ShortDate"": ""2017-01-01"",
    ""LongDate"": ""01 January 2017""
}";

            var serialisationSettings = new JsonSerializerSettings();
            //NodaTime default converter supports ShortDate format
            serialisationSettings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);

            //Exception on LongDate property
            var deserialisedExample1 = JsonConvert.DeserializeObject<ExampleDatedModel>(exampleJsonString, serialisationSettings);

            serialisationSettings.Converters.Remove(NodaConverters.LocalDateConverter);
            serialisationSettings.Converters.Add(new NodaPatternConverter<LocalDate>(LocalDatePattern.CreateWithInvariantCulture("dd MMMM yyyy")));

            //Exception on ShortDate property
            var deserialisedExample2 = JsonConvert.DeserializeObject<ExampleDatedModel>(exampleJsonString, serialisationSettings);
        }
    }
}

使用默认序列化程序会在 LongDate 上引发异常 属性:

An unhandled exception of type 'NodaTime.Text.UnparsableValueException' occurred in Newtonsoft.Json.dll

Additional information: The value string does not match the required number from the format string "yyyy". Value being parsed: '^01 January 2017'. (^ indicates error position.)

替换为自定义模式转换器会在 ShortDate 上引发异常 属性:

An unhandled exception of type 'NodaTime.Text.UnparsableValueException' occurred in Newtonsoft.Json.dll

Additional information: The value string does not match a simple character in the format string " ". Value being parsed: '20^17-01-01'. (^ indicates error position.)

原则上我认为我可以为两个属性使用两个不同的转换器,例如

class ExampleDatedModel
{
    [JsonConverter(typeof(ShortDateConverter)]
    public LocalDate ShortDate { get; set; }

    [JsonConverter(typeof(LongDateConverter)]
    public LocalDate LongDate { get; set; }
}

但是我看不到如何将 NodaTime 的 NodaPatternConverter 与属性一起使用,因为您无法使用模式实例化转换器。

documentation 很有帮助地说 "Custom converters can be created easily from patterns using NodaPatternConverter." 但没有给出任何例子!

我考虑过的可能解决方案是

但我希望我只是缺少一种标记资源 classes 以使用现有转换器的方法。

这似乎确实是我们没有考虑过的用例。对于 "normal" 用法,密封 NodaPatternConverter 感觉是正确的方法 - 但是当 JsonConverter 必须由 type 指定而不是实例化时,密封令人沮丧。我已经 filed an issue to fix this in 2.0, which I'm hoping to release in the next month or so. (It's now implemented - 拉取请求也显示了示例用法。)

然而,与此同时,我可能 只是 fork NodaPatternConverter - 并添加一条评论说它只在那里,直到你可以使用 2.0.

您可能希望 trim 降低一点,因为您可能不需要额外的验证,假设您控制所有将序列化数据的代码 - 如果您不需要担心关于 non-ISO LocalDate 值,您可能不需要验证。

另一方面是,如果您只是 解析 使用转换器,则根本不需要写入端 - 您可以为此抛出一个异常目前,可能。

解封 NodaPatternConverter 的替代方法是使用简单(抽象)DelegatingConverterBase 类型,该类型委托给另一个 JsonConverter。典型用法类似于:

public sealed class ShortDateConverter : DelegatingConverterBase
{
    public ShortDateConverter() : base(NodaConverters.LocalDate) {}
}

这可能是更优雅的关注点分离 - 并且在成为 Noda Time 的一部分之前可以用更少的代码实现:

public abstract class DelegatingConverterBase : JsonConverter
{
    private readonly JsonConverter original;

    protected DelegatingConverterBase(JsonConverter original)
    {
        this.original = original;
    }

    public override void WriteJson(
        JsonWriter writer, object value, JsonSerializer serializer) =>
        original.WriteJson(writer, value, serializer);

    public override object ReadJson(
        JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
        original.ReadJson(reader, objectType, existingValue, serializer);

    public override bool CanRead => original.CanRead;

    public override bool CanWrite => original.CanWrite;

    public override bool CanConvert(Type objectType) => original.CanConvert(objectType);
}