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

时间:2017-02-20 17:52:58

标签: c# json.net nodatime

我尝试使用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属性上抛出异常:

  

未处理的类型异常   &#39; NodaTime.Text.UnparsableValueException&#39;发生在   Newtonsoft.Json.dll

     

其他信息:值字符串与所需值不匹配   来自格式字符串&#34; yyyy&#34;的数字。正在解析的价值:&#39; ^ 01月1日   2017&#39 ;. (^表示错误位置。)

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

  

未处理的类型异常   &#39; NodaTime.Text.UnparsableValueException&#39;发生在   Newtonsoft.Json.dll

     

附加信息:值字符串与简单字符串不匹配   格式字符串中的字符&#34; &#34 ;.正在解析的价值:&#39; 20 ^ 17-01-01&#39;。   (^表示错误位置。)

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

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

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

但是我不知道如何使用NodaTime的NodaPatternConverter和属性,因为你可以用模式实例化转换器。

documentation有用地说&#34;可以使用NodaPatternConverter轻松地从模式创建自定义转换器。&#34;但是没有给出任何例子!

我可能的解决方案

  • 创建一对从NodaPatternConverter派生的转换器,这两个转换器是为这两种模式配置的。
    • NodaPatternConverter是密封的,因此无法继承。
  • 创建一对从JsonConverter派生的转换器来处理这两种模式
    • 我猜这些会在内部调用两个版本的LocalDatePattern.Parse和不同的模式。
    • 整个JsonConverter的重新实现似乎有点过分。
  • 将日期反序列化为字符串,然后转换日期。
    • 需要为每个类型混合的类实现。
    • 使从API获取资源的通用方法更加复杂。

但我希望我只是错过了一种标记资源类以使用现有转换器的方法。

1 个答案:

答案 0 :(得分:3)

这确实似乎是我们没有考虑过的用例。对于“正常”使用,密封NodaPatternConverter感觉就像是正确的方法 - 但是当类型必须指定JsonConverter而不是实例化时,密封是令人沮丧的。我filed an issue要在2.0中解决这个问题,我希望在下个月左右发布。 (它是now implemented - 拉取请求也显示了样本用法。)

然而,与此同时,我可能只是分叉NodaPatternConverter - 并添加一条评论,说明只有在你可以使用2.0之前。

你可能想稍微调整一下,因为你可能不需要额外的验证,假设你控制了所有要序列化数据的代码 - 如果你不需要担心非ISO {{ 1}}值,您可能不需要验证。

另一个方面是,如果你只是使用转换器解析,你根本不需要写作方 - 你可能只是为此抛出异常,可能

开封LocalDate的替代方法是使用简单的(抽象)NodaPatternConverter类型,该类型委托给另一个DelegatingConverterBase。典型用法如下:

JsonConverter

这可能是一个更优雅的问题分离 - 并且可以用更少的代码实现,直到它也是Noda Time的一部分:

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