测试自定义 JsonConverter 时出现异常
Exception when testing custom JsonConverter
我们从 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; }
}
}
不幸的是,在尝试执行单元测试时,我在 var dateTimeString = reader.GetString();
处得到一个 InvalidOperationException
System.InvalidOperationException: 'Cannot get the value of a token type 'None' as a string.'
如何正确设置测试/我做错了什么?
之前你可以调用JsonConverter<T>.Read()
you must advance the 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});
}
演示 fiddle #1 here.
或者,您可以通过解析简单的 JSON 字符串文字 "/Date(1573581177000)/"
来简化单元测试。但是,您仍然需要将 reader 前进一次,因为它最初位于第一个标记的开头之前, 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});
}
演示 fiddle #2 here.
备注:
"\/Date(number of ticks)\/"
是 Microsoft 最初的 JavaScriptSerializer
用于将 DateTime
序列化为 JSON 字符串的格式。有关详细信息,请参阅 the documentation remarks。
(在 JSON string literal, \/
is just an escaped \
and will be silently interpreted as such by Utf8JsonReader
, see fiddle #3 here 中。您不需要在 JSON 转换器中检查 \/
来处理 JavaScriptSerializer
生成的日期和次。)
DataContractSerializer
使用的格式略有不同。来自 docs:
DateTime values appear as JSON strings in the form of "/Date(700000+0500)/", where the first number (700000 in the example provided) is the number of milliseconds in the GMT time zone, regular (non-daylight savings) time since midnight, January 1, 1970. The number may be negative to represent earlier times. The part that consists of "+0500" in the example is optional and indicates that the time is of the Local kind - that is, should be converted to the local time zone on deserialization. If it is absent, the time is deserialized as Utc. The actual number ("0500" in this example) and its sign (+ or -) are ignored.
Newtonsoft 的 DateTimeUtils.TryParseDateTimeMicrosoft()
实施可能有助于指导您的实施。
我们从 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; }
}
}
不幸的是,在尝试执行单元测试时,我在 var dateTimeString = reader.GetString();
InvalidOperationException
System.InvalidOperationException: 'Cannot get the value of a token type 'None' as a string.'
如何正确设置测试/我做错了什么?
之前你可以调用JsonConverter<T>.Read()
you must advance the 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});
}
演示 fiddle #1 here.
或者,您可以通过解析简单的 JSON 字符串文字 "/Date(1573581177000)/"
来简化单元测试。但是,您仍然需要将 reader 前进一次,因为它最初位于第一个标记的开头之前, 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});
}
演示 fiddle #2 here.
备注:
"\/Date(number of ticks)\/"
是 Microsoft 最初的JavaScriptSerializer
用于将DateTime
序列化为 JSON 字符串的格式。有关详细信息,请参阅 the documentation remarks。
(在 JSON string literal, \/
is just an escaped \
and will be silently interpreted as such by Utf8JsonReader
, see fiddle #3 here 中。您不需要在 JSON 转换器中检查 \/
来处理 JavaScriptSerializer
生成的日期和次。)
DataContractSerializer
使用的格式略有不同。来自 docs:DateTime values appear as JSON strings in the form of "/Date(700000+0500)/", where the first number (700000 in the example provided) is the number of milliseconds in the GMT time zone, regular (non-daylight savings) time since midnight, January 1, 1970. The number may be negative to represent earlier times. The part that consists of "+0500" in the example is optional and indicates that the time is of the Local kind - that is, should be converted to the local time zone on deserialization. If it is absent, the time is deserialized as Utc. The actual number ("0500" in this example) and its sign (+ or -) are ignored.
Newtonsoft 的
DateTimeUtils.TryParseDateTimeMicrosoft()
实施可能有助于指导您的实施。