How to use Utf8JsonReader in (Jak używać utf8JsonReader w programie System.Text.Json
W tym artykule pokazano, jak używać Utf8JsonReader typu do tworzenia niestandardowych analizatorów i deserializacji.
Utf8JsonReader to wysokowydajny, niski przydział, czytnik tylko do przodu dla tekstu JSON zakodowanego w formacie UTF-8, odczytywany z elementu ReadOnlySpan<byte>
lub ReadOnlySequence<byte>
. Jest Utf8JsonReader
to typ niskiego poziomu, który może służyć do tworzenia niestandardowych parserów i deserializatorów. Metody JsonSerializer.Deserialize są używane Utf8JsonReader
w ramach okładek.
Utf8JsonReader
Nie można używać bezpośrednio z poziomu programu Visual Basic Code. Aby uzyskać więcej informacji, zobacz Obsługa języka Visual Basic.
W poniższym przykładzie pokazano, jak używać Utf8JsonReader klasy:
var options = new JsonReaderOptions
{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip
};
var reader = new Utf8JsonReader(jsonUtf8Bytes, options);
while (reader.Read())
{
Console.Write(reader.TokenType);
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
case JsonTokenType.String:
{
string? text = reader.GetString();
Console.Write(" ");
Console.Write(text);
break;
}
case JsonTokenType.Number:
{
int intValue = reader.GetInt32();
Console.Write(" ");
Console.Write(intValue);
break;
}
// Other token types elided for brevity
}
Console.WriteLine();
}
' This code example doesn't apply to Visual Basic. For more information, go to the following URL:
' https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support
Powyższy kod zakłada, że jsonUtf8
zmienna jest tablicą bajtów zawierającą prawidłowy kod JSON zakodowany jako UTF-8.
Filtrowanie danych przy użyciu Utf8JsonReader
W poniższym przykładzie pokazano, jak synchronicznie odczytać plik i wyszukać wartość.
using System.Text;
using System.Text.Json;
namespace SystemTextJsonSamples
{
public class Utf8ReaderFromFile
{
private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");
private static ReadOnlySpan<byte> Utf8Bom => new byte[] { 0xEF, 0xBB, 0xBF };
public static void Run()
{
// ReadAllBytes if the file encoding is UTF-8:
string fileName = "UniversitiesUtf8.json";
ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);
// Read past the UTF-8 BOM bytes if a BOM exists.
if (jsonReadOnlySpan.StartsWith(Utf8Bom))
{
jsonReadOnlySpan = jsonReadOnlySpan.Slice(Utf8Bom.Length);
}
// Or read as UTF-16 and transcode to UTF-8 to convert to a ReadOnlySpan<byte>
//string fileName = "Universities.json";
//string jsonString = File.ReadAllText(fileName);
//ReadOnlySpan<byte> jsonReadOnlySpan = Encoding.UTF8.GetBytes(jsonString);
int count = 0;
int total = 0;
var reader = new Utf8JsonReader(jsonReadOnlySpan);
while (reader.Read())
{
JsonTokenType tokenType = reader.TokenType;
switch (tokenType)
{
case JsonTokenType.StartObject:
total++;
break;
case JsonTokenType.PropertyName:
if (reader.ValueTextEquals(s_nameUtf8))
{
// Assume valid JSON, known schema
reader.Read();
if (reader.GetString()!.EndsWith("University"))
{
count++;
}
}
break;
}
}
Console.WriteLine($"{count} out of {total} have names that end with 'University'");
}
}
}
' This code example doesn't apply to Visual Basic. For more information, go to the following URL:
' https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support
Aby zapoznać się z asynchroniczną wersją tego przykładu, zobacz projekt JSON przykładów dla platformy .NET.
Powyższy kod ma następujące działanie:
Przyjęto założenie, że kod JSON zawiera tablicę obiektów, a każdy obiekt może zawierać właściwość "name" ciągu typu.
Zlicza obiekty i wartości właściwości "name", które kończą się ciągiem "Uniwersytet".
Zakłada, że plik jest zakodowany jako UTF-16 i transkoduje go do formatu UTF-8. Plik zakodowany jako UTF-8 można odczytać bezpośrednio do obiektu
ReadOnlySpan<byte>
przy użyciu następującego kodu:ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);
Jeśli plik zawiera znacznik kolejności bajtów UTF-8 (BOM), usuń go przed przekazaniem bajtów do
Utf8JsonReader
elementu , ponieważ czytelnik oczekuje tekstu. W przeciwnym razie model BOM jest uznawany za nieprawidłowy kod JSON, a czytelnik zgłasza wyjątek.
Oto przykładowy kod JSON, który można odczytać z poprzedniego kodu. Wynikowy komunikat podsumowania to "2 na 4 mają nazwy, które kończą się ciągiem "University":
[
{
"web_pages": [ "https://contoso.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "contoso.edu" ],
"name": "Contoso Community College"
},
{
"web_pages": [ "http://fabrikam.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "fabrikam.edu" ],
"name": "Fabrikam Community College"
},
{
"web_pages": [ "http://www.contosouniversity.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "contosouniversity.edu" ],
"name": "Contoso University"
},
{
"web_pages": [ "http://www.fabrikamuniversity.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "fabrikamuniversity.edu" ],
"name": "Fabrikam University"
}
]
Odczytywanie ze strumienia przy użyciu polecenia Utf8JsonReader
Podczas odczytywania dużego pliku (na przykład rozmiaru gigabajta lub większej ilości) możesz uniknąć konieczności ładowania całego pliku do pamięci jednocześnie. W tym scenariuszu można użyć elementu FileStream.
W przypadku używania elementu Utf8JsonReader
do odczytu ze strumienia obowiązują następujące reguły:
- Bufor zawierający częściowy ładunek JSON musi być co najmniej tak duży, jak największy w nim token JSON, aby umożliwić czytelnikowi postęp.
- Bufor musi być co najmniej tak duży, jak największa sekwencja białych znaków w formacie JSON.
- Czytelnik nie śledzi danych, które odczytuje, dopóki nie zostanie całkowicie odczytany w ładunku TokenType JSON. Więc gdy w buforze są pozostawione bajty, należy je ponownie przekazać do czytnika. Możesz użyć BytesConsumed polecenia , aby określić, ile bajtów pozostało.
Poniższy kod ilustruje sposób odczytywania ze strumienia. W przykładzie pokazano element MemoryStream. Podobny kod będzie działać z elementem FileStream, z wyjątkiem sytuacji, gdy element FileStream
zawiera kod UTF-8 BOM na początku. W takim przypadku należy usunąć te trzy bajty z buforu przed przekazaniem pozostałych bajtów do .Utf8JsonReader
W przeciwnym razie czytelnik zgłosi wyjątek, ponieważ model BOM nie jest uważany za prawidłową część kodu JSON.
Przykładowy kod rozpoczyna się od buforu o rozmiarze 4 KB i podwaja rozmiar buforu za każdym razem, gdy stwierdza, że rozmiar nie jest wystarczająco duży, aby zmieścić pełny token JSON, który jest wymagany dla czytelnika, aby kontynuować postęp w ładunku JSON. Przykład JSON podany w fragmencie kodu wyzwala wzrost rozmiaru buforu tylko wtedy, gdy ustawisz bardzo mały rozmiar buforu początkowego, na przykład 10 bajtów. W przypadku ustawienia początkowego rozmiaru buforu na 10 Console.WriteLine
instrukcje ilustrują przyczynę i wpływ rozmiaru buforu. Przy początkowym rozmiarze buforu o rozmiarze 4 KB cały przykładowy kod JSON jest wyświetlany przez każdy Console.WriteLine
element , a rozmiar buforu nigdy nie musi być zwiększany.
using System.Text;
using System.Text.Json;
namespace SystemTextJsonSamples
{
public class Utf8ReaderPartialRead
{
public static void Run()
{
var jsonString = @"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""Temperature"": 25,
""TemperatureRanges"": {
""Cold"": { ""High"": 20, ""Low"": -10 },
""Hot"": { ""High"": 60, ""Low"": 20 }
},
""Summary"": ""Hot"",
}";
byte[] bytes = Encoding.UTF8.GetBytes(jsonString);
var stream = new MemoryStream(bytes);
var buffer = new byte[4096];
// Fill the buffer.
// For this snippet, we're assuming the stream is open and has data.
// If it might be closed or empty, check if the return value is 0.
stream.Read(buffer);
// We set isFinalBlock to false since we expect more data in a subsequent read from the stream.
var reader = new Utf8JsonReader(buffer, isFinalBlock: false, state: default);
Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");
// Search for "Summary" property name
while (reader.TokenType != JsonTokenType.PropertyName || !reader.ValueTextEquals("Summary"))
{
if (!reader.Read())
{
// Not enough of the JSON is in the buffer to complete a read.
GetMoreBytesFromStream(stream, ref buffer, ref reader);
}
}
// Found the "Summary" property name.
Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");
while (!reader.Read())
{
// Not enough of the JSON is in the buffer to complete a read.
GetMoreBytesFromStream(stream, ref buffer, ref reader);
}
// Display value of Summary property, that is, "Hot".
Console.WriteLine($"Got property value: {reader.GetString()}");
}
private static void GetMoreBytesFromStream(
MemoryStream stream, ref byte[] buffer, ref Utf8JsonReader reader)
{
int bytesRead;
if (reader.BytesConsumed < buffer.Length)
{
ReadOnlySpan<byte> leftover = buffer.AsSpan((int)reader.BytesConsumed);
if (leftover.Length == buffer.Length)
{
Array.Resize(ref buffer, buffer.Length * 2);
Console.WriteLine($"Increased buffer size to {buffer.Length}");
}
leftover.CopyTo(buffer);
bytesRead = stream.Read(buffer.AsSpan(leftover.Length));
}
else
{
bytesRead = stream.Read(buffer);
}
Console.WriteLine($"String in buffer is: {Encoding.UTF8.GetString(buffer)}");
reader = new Utf8JsonReader(buffer, isFinalBlock: bytesRead == 0, reader.CurrentState);
}
}
}
' This code example doesn't apply to Visual Basic. For more information, go to the following URL:
' https://learn.microsoft.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support
W poprzednim przykładzie nie określono limitu wielkości buforu. Jeśli rozmiar tokenu jest zbyt duży, kod może zakończyć się niepowodzeniem OutOfMemoryException z wyjątkiem. Może się tak zdarzyć, jeśli plik JSON zawiera token o rozmiarze około 1 GB lub większym, ponieważ podwojenie rozmiaru 1 GB powoduje, że rozmiar jest zbyt duży, aby zmieścić się w buforze int32
.
ograniczenia struktury ref
Utf8JsonReader
Ponieważ typ jest strukturą ref, ma pewne ograniczenia. Na przykład nie można go przechowywać jako pola w klasie lub strukturę inną niż struktura ref.
Aby osiągnąć wysoką wydajność, ten typ musi być typu ref struct
, ponieważ musi buforować wejściowy bajt> ReadOnlySpan<, który sam jest strukturą ref. Ponadto typ jest modyfikowalny, Utf8JsonReader
ponieważ przechowuje stan. W związku z tym należy przekazać go przez odwołanie, a nie przez wartość. Przekazanie jej według wartości spowoduje skopiowanie struktury, a zmiany stanu nie będą widoczne dla elementu wywołującego.
Aby uzyskać więcej informacji na temat używania struktur ref, zobacz Unikanie alokacji.
Odczytywanie tekstu UTF-8
Aby osiągnąć najlepszą możliwą wydajność podczas korzystania z polecenia Utf8JsonReader
, odczyt ładunków JSON już zakodowanych jako tekst UTF-8, a nie jako ciągi UTF-16. Aby zapoznać się z przykładem kodu, zobacz Filter data using Utf8JsonReader (Filtrowanie danych przy użyciu elementu Utf8JsonReader).
Odczyt za pomocą funkcji ReadOnlySequence z wieloma segmentami
Jeśli dane wejściowe JSON są bajtem >ReadOnlySpan<, dostęp do każdego elementu JSON można uzyskać z ValueSpan
właściwości czytnika podczas przechodzenia przez pętlę odczytu. Jeśli jednak dane wejściowe są bajtem > ReadOnlySequence<(co jest wynikiem odczytu z PipeReaderelementu ), niektóre elementy JSON mogą zawierać wiele segmentów ReadOnlySequence<byte>
obiektu. Te elementy nie będą dostępne w ValueSpan ciągłym bloku pamięci. Zamiast tego za każdym razem, gdy masz wiele segmentów ReadOnlySequence<byte>
jako dane wejściowe, sonduj HasValueSequence właściwość na czytniku, aby dowiedzieć się, jak uzyskać dostęp do bieżącego elementu JSON. Oto zalecany wzorzec:
while (reader.Read())
{
switch (reader.TokenType)
{
// ...
ReadOnlySpan<byte> jsonElement = reader.HasValueSequence ?
reader.ValueSequence.ToArray() :
reader.ValueSpan;
// ...
}
}
Użyj właściwości ValueTextEquals, aby wyszukać nazwy właściwości
Nie używaj ValueSpan do porównywania bajtów bajtów po bajtach, wywołując SequenceEqual wyszukiwanie nazw właściwości. Wywołaj ValueTextEquals zamiast tego metodę, ponieważ ta metoda usuwa wszystkie znaki, które są ucieczki w formacie JSON. Oto przykład pokazujący, jak wyszukać właściwość o nazwie "name":
private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.StartObject:
total++;
break;
case JsonTokenType.PropertyName:
if (reader.ValueTextEquals(s_nameUtf8))
{
count++;
}
break;
}
}
Odczytywanie wartości null do typów wartości dopuszczanych do wartości null
Wbudowane System.Text.Json
interfejsy API zwracają tylko typy wartości innych niż null. Na przykład Utf8JsonReader.GetBoolean zwraca wartość bool
. Zgłasza wyjątek w przypadku znalezienia Null
go w formacie JSON. W poniższych przykładach pokazano dwa sposoby obsługi wartości null: jeden, zwracając typ wartości dopuszczającej wartość null i jeden, zwracając wartość domyślną:
public bool? ReadAsNullableBoolean()
{
_reader.Read();
if (_reader.TokenType == JsonTokenType.Null)
{
return null;
}
if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False)
{
throw new JsonException();
}
return _reader.GetBoolean();
}
public bool ReadAsBoolean(bool defaultValue)
{
_reader.Read();
if (_reader.TokenType == JsonTokenType.Null)
{
return defaultValue;
}
if (_reader.TokenType != JsonTokenType.True && _reader.TokenType != JsonTokenType.False)
{
throw new JsonException();
}
return _reader.GetBoolean();
}
Pomiń elementy podrzędne tokenu
Utf8JsonReader.Skip() Użyj metody , aby pominąć elementy podrzędne bieżącego tokenu JSON. Jeśli typ tokenu to JsonTokenType.PropertyName, czytelnik przechodzi do wartości właściwości. Poniższy fragment kodu przedstawia przykład użycia w Utf8JsonReader.Skip() celu przeniesienia czytnika do wartości właściwości.
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};
byte[] jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(weatherForecast);
var reader = new Utf8JsonReader(jsonUtf8Bytes);
int temp;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
{
if (reader.ValueTextEquals("TemperatureCelsius"))
{
reader.Skip();
temp = reader.GetInt32();
Console.WriteLine($"Temperature is {temp} degrees.");
}
continue;
}
default:
continue;
}
}
Używanie zdekodowanych ciągów JSON
Począwszy od platformy .NET 7, można użyć Utf8JsonReader.CopyString metody zamiast Utf8JsonReader.GetString() używać zdekodowanego ciągu JSON. W przeciwieństwie do GetString()elementu , który zawsze przydziela nowy ciąg, CopyString umożliwia skopiowanie niezasłanianego ciągu do buforu, którego jesteś właścicielem. Poniższy fragment kodu przedstawia przykład korzystania z ciągu UTF-16 przy użyciu polecenia CopyString.
var reader = new Utf8JsonReader( /* jsonReadOnlySpan */ );
int valueLength = reader.HasValueSequence
? checked((int)reader.ValueSequence.Length)
: reader.ValueSpan.Length;
char[] buffer = ArrayPool<char>.Shared.Rent(valueLength);
int charsRead = reader.CopyString(buffer);
ReadOnlySpan<char> source = buffer.AsSpan(0, charsRead);
// Handle the unescaped JSON string.
ParseUnescapedString(source);
ArrayPool<char>.Shared.Return(buffer, clearArray: true);
void ParseUnescapedString(ReadOnlySpan<char> source)
{
// ...
}
Powiązane interfejsy API
Aby wykonać deserializowanie typu niestandardowego z wystąpienia, wywołaj metodę
Utf8JsonReader
JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonSerializerOptions) lub JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonTypeInfo<TValue>). Aby zapoznać się z przykładem, zobacz Deserialize z formatu UTF-8.JsonNode oraz klasy, które pochodzą z niego, zapewniają możliwość tworzenia modyfikowalnego modelu DOM. Wystąpienie można przekonwertować
Utf8JsonReader
naJsonNode
wystąpienie, wywołując polecenie JsonNode.Parse(Utf8JsonReader, Nullable<JsonNodeOptions>). Poniższy fragment kodu przedstawia przykład.using System.Text.Json; using System.Text.Json.Nodes; namespace Utf8ReaderToJsonNode { public class WeatherForecast { public DateTimeOffset Date { get; set; } public int TemperatureCelsius { get; set; } public string? Summary { get; set; } } public class Program { public static void Main() { var weatherForecast = new WeatherForecast { Date = DateTime.Parse("2019-08-01"), TemperatureCelsius = 25, Summary = "Hot" }; byte[] jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(weatherForecast); var utf8Reader = new Utf8JsonReader(jsonUtf8Bytes); JsonNode? node = JsonNode.Parse(ref utf8Reader); Console.WriteLine(node); } } }
JsonDocument zapewnia możliwość tworzenia modelu DOM tylko do odczytu przy użyciu polecenia
Utf8JsonReader
. Wywołaj metodę JsonDocument.ParseValue(Utf8JsonReader) , aby przeanalizować elementJsonDocument
zUtf8JsonReader
wystąpienia. Dostęp do elementów JSON tworzących ładunek można uzyskać za pomocą JsonElement typu . Na przykład kod, który używa JsonDocument.ParseValue(Utf8JsonReader)metody , zobacz RoundtripDataTable.cs i fragment kodu w deserialize wnioskowania typów do właściwości obiektów.Możesz również przeanalizować
Utf8JsonReader
wystąpienie do JsonElementklasy , która reprezentuje określoną wartość JSON, wywołując metodę JsonElement.ParseValue(Utf8JsonReader).
Zobacz też
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla