Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Tento článek ukazuje, jak můžete použít Utf8JsonReader typ pro vytváření vlastních analyzátorů a deserializérů.
Utf8JsonReader je vysoce výkonný, paměťově úsporný, jednosměrný čtenář pro JSON text kódovaný v UTF-8. Text se čte z ReadOnlySpan<byte> nebo z ReadOnlySequence<byte>.
Utf8JsonReader je typ nízké úrovně, který lze použít k vytváření vlastních analyzátorů a deserializérů. (Metody JsonSerializer.Deserialize používají Utf8JsonReader na pozadí.)
Následující příklad ukazuje, jak používat Utf8JsonReader třídu. Tento kód předpokládá, že jsonUtf8Bytes proměnná je bajtové pole, které obsahuje platný kód JSON kódovaný jako UTF-8.
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
Poznámka:
Utf8JsonReader nelze použít přímo z kódu jazyka Visual Basic. Další informace naleznete v tématu Podpora jazyka Visual Basic.
Filtrování dat pomocí Utf8JsonReader
Následující příklad ukazuje, jak synchronně číst soubor a hledat hodnotu.
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
Předchozí kód:
Předpokládá, že JSON obsahuje pole objektů a každý objekt může obsahovat vlastnost "name" typu string.
Počítá objekty a hodnoty vlastnosti "name", které končí na "University".
Předpokládá, že soubor je kódovaný jako UTF-16 a překóduje ho do UTF-8.
Soubor kódovaný jako UTF-8 lze číst přímo do souboru
ReadOnlySpan<byte>pomocí následujícího kódu:ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);Pokud soubor obsahuje značku pořadí bajtů UTF-8 (BOM), odeberte ho před předáním bajtů do objektu
Utf8JsonReader, protože čtenář očekává text. V opačném případě se bom považuje za neplatný JSON a čtenář vyvolá výjimku.
Tady je ukázka JSON, kterou může předchozí kód přečíst. Výsledná souhrnná zpráva je "2 ze 4 mají jména, která končí na '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"
}
]
Tip
Asynchronní verzi tohoto příkladu najdete v ukázkovém projektu JSON pro .NET.
Čtení ze streamu pomocí Utf8JsonReader
Při čtení velkého souboru (například gigabajtu nebo více), můžete se vyhnout načtení celého souboru do paměti najednou. Pro tento scénář můžete použít FileStream.
Při použití Utf8JsonReader ke čtení z datového proudu platí následující pravidla:
- Buffer obsahující částečné JSON zatížení musí být alespoň tak velký jako největší token JSON v něm, aby čtenář mohl pokračovat.
- Vyrovnávací paměť musí být alespoň tak velká jako největší posloupnost prázdných znaků v rámci JSON.
- Čtenář si nezaznamená data, která přečetl, dokud nepřečte kompletně další TokenType v datové části JSON. Takže když v vyrovnávací paměti zůstanou bajty, musíte je znovu předat čtenáři. Můžete použít BytesConsumed k určení, kolik bajtů zbývá.
Následující kód ukazuje, jak číst z datového proudu. Příklad ukazuje MemoryStream. Podobný kód bude fungovat s FileStream, kromě případů, kdy FileStream obsahuje BOM UTF-8 na začátku. V takovém případě je nutné tyto tři bajty z vyrovnávací paměti odstranit před předáním zbývajících bajtů do Utf8JsonReader. V opačném případě by čtečka vyvolala výjimku, protože BOM se nepovažuje za platnou část JSON.
Vzorek kódu začíná s vyrovnávací pamětí o velikosti 4 kB a pokaždé zdvojnásobí velikost paměti, když zjistí, že aktuální velikost není dostatečně velká na to, aby pojala kompletní token JSON, který je potřebný k tomu, aby čtenář mohl pokračovat ve zpracování JSON zátěže. Ukázka JSON poskytnutá ve fragmentu kódu aktivuje zvětšení velikosti vyrovnávací paměti pouze v případě, že nastavíte velmi malou počáteční velikost vyrovnávací paměti, například 10 bajtů. Pokud nastavíte počáteční velikost vyrovnávací paměti na 10, Console.WriteLine příkazy znázorňují příčinu a vliv zvětšení velikosti vyrovnávací paměti. Při počáteční velikosti vyrovnávací paměti 4 kB se při každém volání Console.WriteLinezobrazí celý ukázkový kód JSON a velikost vyrovnávací paměti se nikdy nemusí zvětšit.
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
Předchozí příklad nenastavuje žádné omezení, jak velká vyrovnávací paměť může růst. Pokud je velikost tokenu příliš velká, může kód selhat s OutOfMemoryException výjimkou. K tomu může dojít v případě, že JSON obsahuje token, který má velikost přibližně 1 GB nebo větší, protože při zdvojnásobení velikosti 1 GB dojde k příliš velké velikosti, aby se vešla do int32 vyrovnávací paměti.
omezení struktury ref
Vzhledem k tomu, že Utf8JsonReader typ je ref struct, má určitá omezení. Nelze ji například uložit jako pole v jiné třídě nebo struktuře než ref struct.
Aby bylo možné dosáhnout vysokého výkonu, Utf8JsonReader musí být ref struct, protože musí ukládat vstupní ReadOnlySpan<byte> (což je ref struct). Kromě toho je Utf8JsonReader typ proměnlivý, protože obsahuje stav.
Proto ji předejte odkazem místo podle hodnoty.
Utf8JsonReader Předáním hodnoty by došlo k zkopírování struktury a změny stavu by volajícímu nebylo viditelné.
Další informace o tom, jak používat ref struktury, naleznete v tématu Vyhněte se přidělení.
Čtení textu UTF-8
Pokud chcete dosáhnout nejlepšího možného výkonu při použití Utf8JsonReader, přečtěte si datové části JSON, které jsou už kódované jako text UTF-8, a ne jako řetězce UTF-16. Příklad kódu naleznete v tématu Filtrování dat pomocí Utf8JsonReader.
Čtení pomocí funkce ReadOnlySequence s více segmenty
Pokud je váš vstup JSON typu ReadOnlySpan<byte>, každý prvek JSON může být přístupný z vlastnosti ValueSpan čtečky během iterace smyčky čtení. Pokud je ale vaším vstupem ReadOnlySequence<byte> (což je výsledek čtení z PipeReader), některé prvky JSON mohou přesahovat více segmentů objektu ReadOnlySequence<byte>. Tyto prvky by nebyly přístupné z ValueSpan souvislého bloku paměti. Místo toho, když máte jako vstup více segmentů ReadOnlySequence<byte>, dotazujte se na vlastnost HasValueSequence na čtenáři, abyste zjistili, jak získat přístup k aktuálnímu prvku JSON. Tady je doporučený vzor:
while (reader.Read())
{
switch (reader.TokenType)
{
// ...
ReadOnlySpan<byte> jsonElement = reader.HasValueSequence ?
reader.ValueSequence.ToArray() :
reader.ValueSpan;
// ...
}
}
Přečtěte si více dokumentů JSON
V .NET 9 a novějších verzích můžete číst více dokumentů JSON oddělených prázdnými znaky z jedné vyrovnávací paměti nebo datového proudu. Ve výchozím nastavení Utf8JsonReader vyvolá výjimku, pokud zjistí jakékoli znaky, které nejsou mezerami, na konci prvního dokumentu na nejvyšší úrovni. Toto chování ale můžete nakonfigurovat pomocí příznaku JsonReaderOptions.AllowMultipleValues .
JsonReaderOptions options = new() { AllowMultipleValues = true };
Utf8JsonReader reader = new("null {} 1 \r\n [1,2,3]"u8, options);
reader.Read();
Console.WriteLine(reader.TokenType); // Null
reader.Read();
Console.WriteLine(reader.TokenType); // StartObject
reader.Skip();
reader.Read();
Console.WriteLine(reader.TokenType); // Number
reader.Read();
Console.WriteLine(reader.TokenType); // StartArray
reader.Skip();
Console.WriteLine(reader.Read()); // False
Pokud je AllowMultipleValues nastaveno na true, můžete také číst JSON z payloadů, které obsahují koncová data, jež jsou neplatná jako JSON.
JsonReaderOptions options = new() { AllowMultipleValues = true };
Utf8JsonReader reader = new("[1,2,3] <NotJson/>"u8, options);
reader.Read();
reader.Skip(); // Succeeds.
reader.Read(); // Throws JsonReaderException.
Pokud chcete streamovat více hodnot nejvyšší úrovně, použijte přetížení DeserializeAsyncEnumerable<TValue>(Stream, Boolean, JsonSerializerOptions, CancellationToken) nebo DeserializeAsyncEnumerable<TValue>(Stream, JsonTypeInfo<TValue>, Boolean, CancellationToken). Ve výchozím nastavení DeserializeAsyncEnumerable se pokusí streamovat prvky, které jsou obsaženy v jednom poli JSON nejvyšší úrovně. Pro předávání vícero hodnot nejvyšší úrovně použijte true jako hodnotu pro parametr topLevelValues.
ReadOnlySpan<byte> utf8Json = """[0] [0,1] [0,1,1] [0,1,1,2] [0,1,1,2,3]"""u8;
using var stream = new MemoryStream(utf8Json.ToArray());
var items = JsonSerializer.DeserializeAsyncEnumerable<int[]>(stream, topLevelValues: true);
await foreach (int[] item in items)
{
Console.WriteLine(item.Length);
}
/* This snippet produces the following output:
*
* 1
* 2
* 3
* 4
* 5
*/
Vyhledávání názvů vlastností
Pokud chcete vyhledat názvy vlastností, nepoužívejte ValueSpan k porovnávání bajtu po bajtu voláním SequenceEqual. Místo toho volejte ValueTextEquals, protože tento způsob deeskapuje všechny unikové znaky v JSONu. Tady je příklad, který ukazuje, jak vyhledat vlastnost s názvem "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;
}
}
Čtení hodnot null do nulovatelných typů hodnot
Vestavěná rozhraní System.Text.Json API vrací pouze neanulovatelné typy hodnot. Například Utf8JsonReader.GetBoolean vrátí hodnotu bool. Vyvolá výjimku, pokud ji najde Null ve formátu JSON. Následující příklady ukazují dva způsoby zpracování hodnot null, jeden vrácením typu hodnoty s možnou hodnotou null a druhý vrácením výchozí hodnoty:
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();
}
Přeskočit potomky tokenu
Pomocí Utf8JsonReader.Skip() metody ignorujte podřízené prvky aktuálního JSON tokenu. Pokud je JsonTokenType.PropertyNametyp tokenu , čtenář se přesune na hodnotu vlastnosti. Následující fragment kódu ukazuje příklad použití Utf8JsonReader.Skip() k přesunutí čtenáře na hodnotu vlastnosti.
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;
}
}
Použijte dekódované řetězce JSON
Od .NET 7 můžete použít metodu Utf8JsonReader.CopyString místo metody Utf8JsonReader.GetString() pro zpracování dekódovaného řetězce JSON. Na rozdíl od GetString(), který vždy přidělí nový řetězec, CopyString vám umožňuje zkopírovat neescapovaný řetězec do vyrovnávací paměti, kterou vlastníte. Následující fragment kódu ukazuje příklad využívání řetězce UTF-16 pomocí 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)
{
// ...
}
Související rozhraní API
Chcete-li deserializovat vlastní typ z instance
Utf8JsonReader, zavolejte JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonSerializerOptions) nebo JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonTypeInfo<TValue>). Příklad naleznete v tématu Deserialize z UTF-8.JsonNode a třídy, které jsou z ní odvozeny, poskytují možnost vytvořit proměnlivý DOM. Můžete provést převedení instance
Utf8JsonReadernaJsonNodevoláním JsonNode.Parse(Utf8JsonReader, Nullable<JsonNodeOptions>). Následující fragment kódu ukazuje příklad.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 poskytuje možnost vytvořit dom jen pro čtení pomocí
Utf8JsonReader. Metodu JsonDocument.ParseValue(Utf8JsonReader) zavolejte pro parsováníJsonDocumentz instanceUtf8JsonReader. K elementům JSON, které tvoří datovou část, můžete přistupovat prostřednictvím JsonElement typu. Příklad kódu, který používá JsonDocument.ParseValue(Utf8JsonReader), naleznete v RoundtripDataTable.cs a ukázce kódu v části Deserialize odvozené typy na objektové vlastnosti.Můžete také analyzovat instanci
Utf8JsonReaderna instanci JsonElement, která představuje konkrétní hodnotu JSON, voláním JsonElement.ParseValue(Utf8JsonReader).