gpt4 book ai didi

c# - System.Text.Json 反序列化失败,出现 JsonException "read to much or not enough"

转载 作者:行者123 更新时间:2023-12-03 16:21:14 31 4
gpt4 key购买 nike

此问题适用于 System.Text.Json 的自定义反序列化类。在 .Net Core 3.1 中。

我试图理解为什么自定义反序列化类需要读取到 JSON 流的末尾,即使它已经产生了所需的数据,否则反序列化会失败,JsonException以“阅读太多或不够”结尾。

我通读了 System.Text.Json 的 Microsoft 文档([ 1 ],[ 2 ]),但无法弄清楚。

以下是该文档的示例:

{
"Response": {
"Result": [
{
"Code": "CLF",
"Id": 49,
"Type": "H"
},
{
"Code": "CLF",
"Id": 42,
"Type": "C"
}
]
}
}

DTO 类和反序列化方法定义如下:
public class EntityDto
{
public string Code { get; set; }
public int Id { get; set; }
public string Type { get; set; }
}

// This method is a part of class EntityDtoIEnumerableConverter : JsonConverter<IEnumerable<EntityDto>>
public override IEnumerable<EntityDto> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException("JSON payload expected to start with StartObject token.");
}

while ((reader.TokenType != JsonTokenType.StartArray) && reader.Read()) { }

var eodPostions = JsonSerializer.Deserialize<EntityDto[]>(ref reader, options);

// This loop is required to not get JsonException
while (reader.Read()) { }

return new List<EntityDto>(eodPostions);
}

这是反序列化类的调用方式。
var serializerOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
serializerOptions.Converters.Add(new EntityDtoIEnumerableConverter());

HttpResponseMessage message = await httpClient.GetAsync(requestUrl);
message.EnsureSuccessStatusCode();

var contentStream = await msg.Content.ReadAsStreamAsync();
var result = await JsonSerializer.DeserializeAsync<IEnumerable<EntityDto>>(contentStream, serializerOptions);

当最后一个循环 while (reader.Read()) { } in 反序列化方法不存在,或注释掉,最后一次调用 await JsonSerializer.DeserializeAsync<... JsonException 失败,以 read too much or not enough 结尾.谁能解释为什么?还是有更好的方法来编写这种反序列化?

更新 要使用的第二个代码块 EntityDtoIEnumerableConverter .

最佳答案

读取对象时, JsonConverter<T>.Read() 必须离开Utf8JsonReader定位于 EndObject它最初定位的对象的标记。 (对于数组,原始数组的 EndArray。)
在写 Read() 时解析多级 JSON 的方法,这可以通过记住 CurrentDepth 来完成阅读器在进入时,然后阅读直到 EndObject发现在同一深度。

由于您的EntityDtoIEnumerableConverter.Read()方法似乎试图降低 JSON token 层次结构,直到遇到数组,然后将数组反序列化为 EntityDto[] (基本上剥离了 "Response""Result" 包装器属性),您的代码可以重写如下:

public override IEnumerable<EntityDto> Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException("JSON payload expected to start with StartObject token.");
}

List<EntityDto> list = null;
var startDepth = reader.CurrentDepth;

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject && reader.CurrentDepth == startDepth)
return list;
if (reader.TokenType == JsonTokenType.StartArray)
{
if (list != null)
throw new JsonException("Multiple lists encountered.");
var eodPostions = JsonSerializer.Deserialize<EntityDto[]>(ref reader, options);
(list = new List<EntityDto>(eodPostions.Length)).AddRange(eodPostions);
}
}
throw new JsonException(); // Truncated file or internal error
}

笔记:
  • 在您的原始代码中,您在数组反序列化后立即返回。由于JsonSerializer.Deserialize<EntityDto[]>(ref reader, options)仅将阅读器推进到嵌套数组的末尾,您从未将阅读器推进到所需的对象末端。这导致了您看到的异常。 (当当前对象是根对象时,前进到 JSON 流的末尾似乎也有效,但不适用于嵌套对象。)
  • 当前文档文章 How to write custom converters for JSON serialization (marshalling) in .NET 中未显示任何转换器您链接到的尝试将多个级别的 JSON 扁平化为单个 .Net 对象,因此在实践中似乎没有出现跟踪当前深度的需要。

  • 演示 fiddle here .

    关于c# - System.Text.Json 反序列化失败,出现 JsonException "read to much or not enough",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62147178/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com