如何将“ uuuu'-'MM'-'dd'T'HH':'mm':'ss; FFFFFFFFFo <z + hhmm>”解析为Instant?

时间:2018-10-26 08:53:54

标签: json.net nodatime

我上了这个课:

public class Test
{
    public Instant I { get; set;}
}

IInstant,因为这在语义上是有意义的。

但是,我必须对此反序列化:

{
    "i": "2018-10-25T18:34:11.911+00:00"
}

从这里:

{
    "i": "2018-10-25T18:34:11.911+0000"
}

我该怎么做?

2 个答案:

答案 0 :(得分:4)

使用CustomInstantPattern的方法是一种很好的方法,但是我会避免使用任何.NET日期/时间类型。

相反,我将使用两个OffsetDateTimePattern实例,一个实例带有冒号,一个实例不带有冒号,然后将它们与CompositePattern组合在一起。然后,您可以仅委托给它,在需要时与Instant进行相互转换。

这里是完整的示例代码:

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

class CustomInstantPattern : IPattern<Instant>
{
    private readonly IPattern<OffsetDateTime> offsetDateTimePattern;

    public CustomInstantPattern()
    {
        // Pattern explanation:
        // - o<G> means "use the G Offset pattern" (to hour, minute or second, with colons, format +00 as Z)
        // - o<I> means "use the I Offset pattern" (to hour, minute or second, without colons, format +00 as Z)
        var patternWithColon = OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<G>");
        var patternWithoutColon = OffsetDateTimePattern.CreateWithInvariantCulture("uuuu'-'MM'-'dd'T'HH':'mm':'ss;FFFFFFFFFo<I>");
        offsetDateTimePattern = new CompositePatternBuilder<OffsetDateTime>()
        {
            // The predicates here are for formatting. As the first always
            // returns true, it doesn't really matter what the second does.
            // The intention is that some values might not be formattable with
            // all patterns, but that doesn't apply here.
            { patternWithColon, _ => true },
            { patternWithoutColon, _ => true }
        }.Build();
    }

    public StringBuilder AppendFormat(Instant value, StringBuilder builder) =>
        offsetDateTimePattern.AppendFormat(value.WithOffset(Offset.Zero), builder);

    public string Format(Instant value) =>
        offsetDateTimePattern.Format(value.WithOffset(Offset.Zero));

    public ParseResult<Instant> Parse(string text) =>
        offsetDateTimePattern.Parse(text).Convert(odt => odt.ToInstant());
}

class Entity
{
    public Instant I { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var settings = new JsonSerializerSettings
        {
            DateParseHandling = DateParseHandling.None,
            Converters = { new NodaPatternConverter<Instant>(new CustomInstantPattern()) }
        };
        string json = " { \"i\": \"2018-10-25T18:34:11.911+0000\" }";
        Entity entity = JsonConvert.DeserializeObject<Entity>(json, settings);
        Console.WriteLine(entity.I);
        // Check it works with colons too
        json = " { \"i\": \"2018-10-25T18:34:11.911+00:00\" }";
        entity = JsonConvert.DeserializeObject<Entity>(json, settings);
        Console.WriteLine(entity.I);
    }
}

(这显示了Noda Time的两个问题-一个是嵌入式部分文档对于OffsetDateTime模式是不正确的,另一个是我们没有用于“扩展ISO”的标准模式,非常烦人。我会尽力解决这两个问题。

答案 1 :(得分:0)

我已经能够使用一种特殊的模式来解决它:

public class CustomInstantPattern : IPattern<Instant>
{
    public StringBuilder AppendFormat(Instant value, StringBuilder builder)
    {
        return builder.AppendFormat("s", value.ToDateTimeOffset());
    }

    public string Format(Instant value)
    {
        return value.ToDateTimeOffset().ToString("s");
    }

    public ParseResult<Instant> Parse(string text)
    {
        try
        {
            return ParseResult<Instant>.ForValue(DateTimeOffset.Parse(text, CultureInfo.InvariantCulture).ToInstant());
        }
        catch (Exception ex)
        {
            return ParseResult<Instant>.ForException(() => throw ex);
        }
    }
}

var settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    Converters =
    {
        new NodaPatternConverter<Instant>(new CustomInstantPattern()),
    }
};

var converted = JsonConvert.DeserializeObject<Test>("{ 'I': '2018-10-25T18:34:11.911+0000'}", settings);

这是正确的方法吗?