我编写了自己的 BigIntegerConverter 用于 JSON 序列化/反序列化(.Net System.Text.Json) 在 Read 方法中,我检查是否使用了 ValueSequence
...
string stringValue;
if (reader.HasValueSequence)
{
stringValue = Encoding.UTF8.GetString(reader.ValueSequence);
}
else
{
stringValue = Encoding.UTF8.GetString(reader.ValueSpan);
}
if (BigInteger.TryParse(stringValue, CultureInfo.InvariantCulture, out var result))
{
return result;
}
...
现在我想测试该代码,但到目前为止我只能到达 else 树。根据文档,我假设如果数据足够大,将使用 ValueSequence。 但是,我已经使用像
BigInteger.Pow(new BigInteger(long.MaxValue), 1234);
这样大的 BigIntegers 进行了测试,但仍然无法使用 ValueSequence。
我是不是误会了什么? 有没有办法强制使用 ValueSqeuence 进行测试?
我的测试用例看起来像这样
[Theory]
[MemberData(nameof(GetNotNullTestData))]
public void Read_EntityWithNotNullableBigInteger(string name, BigInteger expected, string value)
{
// Arrange
var json = $$"""{"Name":"{{name}}","NotNullableValue":{{value}}}""";
// Act
var result = JsonSerializer.Deserialize<NotNullableBigIntegerEntity>(json, _options);
// Assert
Assert.NotNull(result);
Assert.Equal(name, result.Name);
Assert.Equal(expected, result.NotNullableValue);
}
问候 迈克尔
Utf8JsonReader
有一个构造函数,它需要一个ReadOnlySequence<byte>
,所以你可以将你的JSON字符串编码为UTF8字节数组,将其分成小块,然后将该块序列转换为ReadOnlySequence<byte>
使用ReadOnlySequenceFactory
从这个答案到使用system.text.json从分块字符串数组中反序列化非常大的json。
首先引入以下工厂类:
// From this answer https://stackoverflow.com/a/61087772 to https://stackoverflow.com/questions/61079767/deserialize-very-large-json-from-a-chunked-array-of-strings-using-system-text-js
public static class ReadOnlySequenceFactory
{
public static ReadOnlySequence<T> AsSequence<T>(this IEnumerable<T []> buffers) => ReadOnlyMemorySegment<T>.Create(buffers.Select(a => new ReadOnlyMemory<T>(a)));
public static ReadOnlySequence<T> AsSequence<T>(this IEnumerable<ReadOnlyMemory<T>> buffers) => ReadOnlyMemorySegment<T>.Create(buffers);
// There is no public concrete implementation of ReadOnlySequenceSegment<T> so we must create one ourselves.
// This is modeled on https://github.com/dotnet/runtime/blob/v5.0.18/src/libraries/System.Text.Json/tests/BufferFactory.cs
// by https://github.com/ahsonkhan
class ReadOnlyMemorySegment<T> : ReadOnlySequenceSegment<T>
{
public static ReadOnlySequence<T> Create(IEnumerable<ReadOnlyMemory<T>> buffers)
{
ReadOnlyMemorySegment<T>? first = null;
ReadOnlyMemorySegment<T>? current = null;
foreach (var buffer in buffers)
{
var next = new ReadOnlyMemorySegment<T> { Memory = buffer };
if (first == null)
first = next;
else
{
current!.Next = next;
next.RunningIndex = current.RunningIndex + current.Memory.Length;
}
current = next;
}
if (first == null)
first = current = new ();
return new ReadOnlySequence<T>(first, 0, current!, current!.Memory.Length);
}
}
}
然后编写你的转换器,例如如下:
public class BigIntegerConverter : JsonConverter<BigInteger>
{
// The actual implementation seems to be in INumberBase<TSelf>.TryParse() so I had to do this to call the method:
static bool TryParse<TSelf>(ReadOnlySpan<byte> utf8Text, out TSelf? value) where TSelf : IUtf8SpanParsable<TSelf>, new() =>
TSelf.TryParse(utf8Text, NumberFormatInfo.InvariantInfo, out value);
public override BigInteger Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException(string.Format("Found token {0} but expected token {1}", reader.TokenType, JsonTokenType.Number ));
var utf8Text = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
if (TryParse<BigInteger>(utf8Text, out var value))
return value;
throw new JsonException();
}
public override void Write(Utf8JsonWriter writer, BigInteger value, JsonSerializerOptions options) =>
writer.WriteRawValue(value.ToString(NumberFormatInfo.InvariantInfo), false);
}
现在您将能够按如下方式编写测试方法,确保您的输入 JSON 被分成小块:
public record NotNullableBigIntegerEntity(string Name, BigInteger NotNullableValue);
JsonSerializerOptions _options = new()
{
Converters = { new BigIntegerConverter() },
};
public void Read_EntityWithNotNullableBigInteger(string name, BigInteger expected, string value)
{
int byteChunkSize = 3;
// Arrange
var json = $$"""{"Name":"{{name}}","NotNullableValue":{{value}}}""";
// Break into chunks
var utf8json = Encoding.UTF8.GetBytes(json);
var sequence = utf8json.Chunk(byteChunkSize).AsSequence();
// Act
var reader = new Utf8JsonReader(sequence);
var result = JsonSerializer.Deserialize<NotNullableBigIntegerEntity>(ref reader, _options);
// Assert
Assert.NotNull(result);
Assert.Equal(name, result?.Name);
Assert.Equal(expected, result?.NotNullableValue);
}
演示小提琴在这里。