NodaTime UnparsableValueException 由于在模式中使用 "Z"
NodaTime UnparsableValueException due to usage of "Z" in pattern
我正在 Java 和 C# 之间交换 JSON 消息(反之亦然)。
在 Java 中,我使用 java.time.Instant (JSR-310) 表示全局时间轴上的一个时间点。为了在 JSON 中创建一个人类可读的 date/time 字符串,我按如下方式转换我的 Instant:
private static final DateTimeFormatter FORMATTER = ofPattern("yyyy-MM-dd'T'HH:mm:ssZ").withZone(ZoneId.systemDefault());
生成以下输出:
2017-04-28T19:54:44-0500
现在,在消息消费者方面 (C#),我编写了一个自定义 Newtonsoft.Json.JsonConverter,它扩展了包含以下重写的 ReadJson() 方法的抽象 JsonCreationConvert class:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.StartArray)
{
return JToken.Load(reader).ToObject<string[]>();
}
reader.DateParseHandling = DateParseHandling.None; // read NodaTime string Instant as is
serializer.Converters.Add(NodaConverters.InstantConverter);
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
var writer = new StringWriter();
serializer.Serialize(writer, jObject);
using (var newReader = new JsonTextReader(new StringReader(writer.ToString())))
{
newReader.Culture = reader.Culture;
newReader.DateParseHandling = reader.DateParseHandling;
newReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
newReader.FloatParseHandling = reader.FloatParseHandling;
serializer.Populate(newReader, target);
}
return target;
}
Create() 是一个抽象方法。
当我现在通过调用将此 JSON 字符串转换为 NodaTime.Instant (v2.0.0) 时:
InstantPattern.General.Parse(creationTime).Value;
我得到这个异常:
NodaTime.Text.UnparsableValueException: The value string does not match a quoted string in the pattern. Value being parsed: '2017-04-28T19:54:44^-0500'. (^ indicates error position.)
如果我传递文本文字 "Z"(因此没有输出偏移量“-0500”并且 Z 被解释为 0 偏移量)NodaTime.Serialization.JsonNet.NodaConverters.InstantConverter 正确读取而不会引发异常。
查看 GeneralPatternImpl 我看到:
internal static readonly InstantPattern GeneralPatternImpl = InstantPattern.CreateWithInvariantCulture("uuuu-MM-ddTHH:mm:ss'Z'");
为什么 InstantConverter 要求偏移量是文本文字?发生这种情况是因为 Instant 与偏移量无关吗?如果是这种情况,那么 InstantConverter 为什么不忽略偏移量而不是抛出异常?我需要编写自定义转换器来解决这个问题吗?
这就像要求将 2017-04-28T19:54:44
解析为 LocalDate
- 我们会悄悄删除额外的信息。从根本上说,您在 Java 中从 Instant
到 String
的转换是 "adding" 信息,它在原始瞬间并不真正存在。您最终得到的实际上是 OffsetDateTime
,而不是 Instant
- 它比 Instant
包含更多信息。
您应该决定您真正关心的信息是什么。如果您只关心时间的瞬间,那么将您的 Java 序列化更改为使用 UTC,它应该以序列化形式以 Z
结束,一切都会好起来的。这就是我建议你做的 - 传播不相关的信息是误导,IMO。
如果您真的关心系统默认时区的偏移量,您对 .withZone(ZoneId.systemDefault())
的调用暗示了 您这样做,那么您应该将其解析为 OffsetDateTime
在 .NET 方面。如果需要,您可以随后将其转换为 Instant
(只需调用 ToInstant()
)。
我正在 Java 和 C# 之间交换 JSON 消息(反之亦然)。
在 Java 中,我使用 java.time.Instant (JSR-310) 表示全局时间轴上的一个时间点。为了在 JSON 中创建一个人类可读的 date/time 字符串,我按如下方式转换我的 Instant:
private static final DateTimeFormatter FORMATTER = ofPattern("yyyy-MM-dd'T'HH:mm:ssZ").withZone(ZoneId.systemDefault());
生成以下输出:
2017-04-28T19:54:44-0500
现在,在消息消费者方面 (C#),我编写了一个自定义 Newtonsoft.Json.JsonConverter,它扩展了包含以下重写的 ReadJson() 方法的抽象 JsonCreationConvert class:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.StartArray)
{
return JToken.Load(reader).ToObject<string[]>();
}
reader.DateParseHandling = DateParseHandling.None; // read NodaTime string Instant as is
serializer.Converters.Add(NodaConverters.InstantConverter);
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
T target = Create(objectType, jObject);
// Populate the object properties
var writer = new StringWriter();
serializer.Serialize(writer, jObject);
using (var newReader = new JsonTextReader(new StringReader(writer.ToString())))
{
newReader.Culture = reader.Culture;
newReader.DateParseHandling = reader.DateParseHandling;
newReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
newReader.FloatParseHandling = reader.FloatParseHandling;
serializer.Populate(newReader, target);
}
return target;
}
Create() 是一个抽象方法。
当我现在通过调用将此 JSON 字符串转换为 NodaTime.Instant (v2.0.0) 时:
InstantPattern.General.Parse(creationTime).Value;
我得到这个异常:
NodaTime.Text.UnparsableValueException: The value string does not match a quoted string in the pattern. Value being parsed: '2017-04-28T19:54:44^-0500'. (^ indicates error position.)
如果我传递文本文字 "Z"(因此没有输出偏移量“-0500”并且 Z 被解释为 0 偏移量)NodaTime.Serialization.JsonNet.NodaConverters.InstantConverter 正确读取而不会引发异常。
查看 GeneralPatternImpl 我看到:
internal static readonly InstantPattern GeneralPatternImpl = InstantPattern.CreateWithInvariantCulture("uuuu-MM-ddTHH:mm:ss'Z'");
为什么 InstantConverter 要求偏移量是文本文字?发生这种情况是因为 Instant 与偏移量无关吗?如果是这种情况,那么 InstantConverter 为什么不忽略偏移量而不是抛出异常?我需要编写自定义转换器来解决这个问题吗?
这就像要求将 2017-04-28T19:54:44
解析为 LocalDate
- 我们会悄悄删除额外的信息。从根本上说,您在 Java 中从 Instant
到 String
的转换是 "adding" 信息,它在原始瞬间并不真正存在。您最终得到的实际上是 OffsetDateTime
,而不是 Instant
- 它比 Instant
包含更多信息。
您应该决定您真正关心的信息是什么。如果您只关心时间的瞬间,那么将您的 Java 序列化更改为使用 UTC,它应该以序列化形式以 Z
结束,一切都会好起来的。这就是我建议你做的 - 传播不相关的信息是误导,IMO。
如果您真的关心系统默认时区的偏移量,您对 .withZone(ZoneId.systemDefault())
的调用暗示了 您这样做,那么您应该将其解析为 OffsetDateTime
在 .NET 方面。如果需要,您可以随后将其转换为 Instant
(只需调用 ToInstant()
)。