이 질문 System.Text.Json
은 .Net Core 3.1의 사용자 지정 역 직렬화 클래스에 적용됩니다 .
사용자 지정 역 직렬화 클래스가 이미 필요한 데이터를 생성 했음에도 불구하고 JSON 스트림의 끝까지 읽어야하는 이유를 이해하려고합니다. 그렇지 않으면 역 직렬화가 JsonException
"너무 많이 읽거나 충분하지 않음"으로 끝나고 실패합니다 .
System.Text.Json
([ 1 ], [ 2 ])에 대한 Microsoft 문서를 읽었 지만 알아낼 수 없었습니다.
다음은 문서의 예입니다.
{
"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);
}
deserialization 클래스가 호출되는 방법은 다음과 같습니다.
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()) { }
deserialization 메서드 의 마지막 루프 가 없거나 주석 처리 된 경우 마지막 호출 await JsonSerializer.DeserializeAsync<...
은으로 JsonException
끝나는으로 실패합니다 read too much or not enough
. 아무도 이유를 설명 할 수 있습니까? 아니면이 역 직렬화를 작성하는 더 좋은 방법이 있습니까?
.NET을 사용 하도록 두 번째 코드 블록을 업데이트 했습니다 EntityDtoIEnumerableConverter
.
개체를 읽을 때 원래 위치했던 개체 의 토큰 에있는 위치를 JsonConverter<T>.Read()
그대로 두어야합니다 . (그리고 배열 의 경우 원본 배열의) 여러 수준의 JSON을 구문 분석 하는 메서드를 작성할 때 입력시 판독기의 를 기억 한 다음 동일한 깊이에서를 찾을 때까지 읽음으로써 이를 수행 할 수 있습니다 .Utf8JsonReader
EndObject
EndArray
Read()
CurrentDepth
EndObject
당신 때문에 EntityDtoIEnumerableConverter.Read()
방법은 그것이로 배열을 직렬화 복원하는에 배열이 발생 될 때까지 JSON 토큰 계층을 내려 노력 것으로 보인다 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 스트림이 끝날 때까지 진행하는 것은 현재 객체가 루트 객체 일 때도 작동 한 것처럼 보이지만 중첩 된 객체에서는 작동하지 않았을 것입니다.)
링크 된 .NET에서 JSON 직렬화 (마샬링) 용 사용자 지정 변환기를 작성하는 방법 에 대한 설명서 문서에 현재 표시된 변환기가 없습니다. 수행하는 동안 여러 수준의 JSON을 단일 .Net 개체로 병합하려고 시도하므로 현재 깊이를 추적하는 것은 실제로 발생하지 않은 것 같습니다.
여기에 데모 바이올린 .
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다