如何在同一个 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." 但没有给出任何例子!
我考虑过的可能解决方案是
- 创建一对从 NodaPatternConverter 派生的转换器,它们是为两种模式配置的。
- NodaPatternConverter 是密封的,所以不能继承。
- 创建一对从 JsonConverter 派生的转换器来处理这两种模式
- 我猜这些会调用两个版本的 LocalDatePattern.Parse,内部具有不同的模式。
- 重新实现整个 JsonConverter 似乎有些矫枉过正。
- 将日期反序列化为字符串,然后再转换日期。
- 需要为每个 class 实现混合类型。
- 使从 API 获取资源的通用方法更加复杂。
但我希望我只是缺少一种标记资源 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);
}
我正在尝试使用 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." 但没有给出任何例子!
我考虑过的可能解决方案是
- 创建一对从 NodaPatternConverter 派生的转换器,它们是为两种模式配置的。
- NodaPatternConverter 是密封的,所以不能继承。
- 创建一对从 JsonConverter 派生的转换器来处理这两种模式
- 我猜这些会调用两个版本的 LocalDatePattern.Parse,内部具有不同的模式。
- 重新实现整个 JsonConverter 似乎有些矫枉过正。
- 将日期反序列化为字符串,然后再转换日期。
- 需要为每个 class 实现混合类型。
- 使从 API 获取资源的通用方法更加复杂。
但我希望我只是缺少一种标记资源 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);
}