我们从API获得序列化的DateTimes,格式如下:/Date(1574487012797)/
为了使用System.Text.Json
反序列化此值,我们编写了自己的JsonConverter
:
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dateTimeString = reader.GetString();
dateTimeString = dateTimeString.Replace("/Date(", "");
dateTimeString = dateTimeString.Replace(")/", "");
var epoch = Convert.ToInt64(dateTimeString);
var dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(epoch);
return dateTimeOffset.UtcDateTime;
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
}
}
我想为此转换器编写一个单元测试。 我尝试了以下内容:
public class DateTimeConverterTest
{
private readonly DateTimeConverter testee;
public DateTimeConverterTest()
{
this.testee = new DateTimeConverter();
}
[Fact]
public void Read_WhenCalledWithSerializedDateTime_ThenReturnDeserializedDateTime()
{
var a = "{\r\n \"PublikationsDatum\": \"/Date(1573581177000)/\" \r\n}";
//var serializedDateTime = "/Date(1573581177000)/";
var utf8JsonReader = new Utf8JsonReader(Encoding.UTF8.GetBytes(a), false, new JsonReaderState(new JsonReaderOptions()));
//utf8JsonReader.TokenType = JsonTokenType.String;
var deserializedDateTime = this.testee.Read(ref utf8JsonReader, typeof(DateTime), new JsonSerializerOptions {IgnoreNullValues = true});
}
private class TestClass
{
public DateTime PublikationsDatum { get; set; }
}
}
不幸的是,当尝试执行单元测试时,我在InvalidOperationException
处得到了var dateTimeString = reader.GetString();
System.InvalidOperationException:'无法获得令牌类型'None'的值作为字符串。'
如何正确设置测试/我在做什么错了?
答案 0 :(得分:1)
在调用JsonConverter<T>.Read()
之前,必须先将Utf8JsonReader
前进,直到它位于"PublikationsDatum"
属性的值上,例如像这样:
public void Read_WhenCalledWithSerializedDateTime_ThenReturnDeserializedDateTime()
{
var a = "{\r\n \"PublikationsDatum\": \"/Date(1573581177000)/\" \r\n}";
var utf8JsonReader = new Utf8JsonReader(Encoding.UTF8.GetBytes(a), false, new JsonReaderState(new JsonReaderOptions()));
while (utf8JsonReader.Read())
if (utf8JsonReader.TokenType == JsonTokenType.String)
break;
var deserializedDateTime = this.testee.Read(ref utf8JsonReader, typeof(DateTime), new JsonSerializerOptions {IgnoreNullValues = true});
}
演示小提琴#1 here。
或者,您可以通过解析简单的JSON字符串文字"/Date(1573581177000)/"
来简化单元测试。但是,您仍然需要使读取器前进一次,因为它最初位于第一个令牌的开头之前,并带有utf8JsonReader.TokenType == JsonTokenType.None
:
public void Read_WhenCalledWithSerializedDateTime_ThenReturnDeserializedDateTime()
{
var a = "\"/Date(1573581177000)/\"";
var utf8JsonReader = new Utf8JsonReader(Encoding.UTF8.GetBytes(a), false, new JsonReaderState(new JsonReaderOptions()));
// Reader always starts out without having read anything yet, so TokenType == JsonTokenType.None initially
Assert.IsTrue(utf8JsonReader.TokenType == JsonTokenType.None);
utf8JsonReader.Read();
var deserializedDateTime = this.testee.Read(ref utf8JsonReader, typeof(DateTime), new JsonSerializerOptions {IgnoreNullValues = true});
}
演示小提琴#2 here。
注意:
"\/Date(number of ticks)\/"
是Microsoft原始JavaScriptSerializer
用来将DateTime
序列化为JSON字符串的格式。有关详细信息,请参见the documentation remarks。
(在JSON string literal中,\/
只是转义的\
,Utf8JsonReader
会默默地解释为此类,请参见小提琴#3 here 。您无需在JSON转换器中检查\/
即可处理JavaScriptSerializer
生成的日期和时间。)
DataContractSerializer
使用的格式略有不同。来自docs:
DateTime值以“ / Date(700000 + 0500)/”的形式显示为JSON字符串,其中第一个数字(在提供的示例中为700000)是GMT时区中的毫秒数,常规(非-从1970年1月1日午夜开始的夏令时。该数字可能为负值,代表较早的时间。在示例中,由“ +0500”组成的部分是可选的,它指示时间属于本地时间-也就是说,应在反序列化时将其转换为本地时区。如果不存在,则将时间反序列化为Utc。实际数字(在此示例中为“ 0500”)及其符号(+或-)将被忽略。
Newtonsoft的DateTimeUtils.TryParseDateTimeMicrosoft()
的实现可能有助于指导您的实现。