Megosztás a következőn keresztül:


Az Utf8JsonReader használata System.Text.Json

Ez a cikk bemutatja, hogyan használhatja a típust Utf8JsonReader egyéni elemzők és deszerializálók készítéséhez.

Utf8JsonReader az UTF-8 kódolt JSON-szövegek nagy teljesítményű, alacsony kiosztású, előre csak továbbítható olvasója, amely egy ReadOnlySpan<byte> vagy ReadOnlySequence<byte>. Ez Utf8JsonReader egy alacsony szintű típus, amely egyéni elemzők és deszerializálók készítésére használható. A JsonSerializer.Deserialize módszerek a fedelek alatt használhatók Utf8JsonReader .

Utf8JsonReader nem használható közvetlenül a Visual Basic-kódból. További információ: Visual Basic-támogatás.

Az alábbi példa az osztály használatát Utf8JsonReader mutatja be:

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

Az előző kód feltételezi, hogy a jsonUtf8 változó egy érvényes JSON-t tartalmazó bájttömb, amely UTF-8 kóddal van kódolva.

Adatok szűrése Utf8JsonReader

Az alábbi példa bemutatja, hogyan olvashatja el szinkron módon a fájlokat, és hogyan kereshet értékeket.

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

A példa aszinkron verzióját a .NET-minták JSON-projektje tartalmazza.

A fenti kód a következőket végzi el:

  • Feltételezi, hogy a JSON objektumtömböt tartalmaz, és minden objektum tartalmazhat egy "name" típusú tulajdonságot.

  • Megszámolja az "Egyetem" végződésű objektumokat és "name" tulajdonságértékeket.

  • Feltételezi, hogy a fájl UTF-16-ként van kódolva, és UTF-8-ra kódolja. Az UTF-8 kóddal kódolt fájlok közvetlenül beolvashatók egy ReadOnlySpan<byte> fájlba a következő kóddal:

    ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);
    

    Ha a fájl UTF-8 bájtos rendelésjelet (BOM) tartalmaz, távolítsa el, mielőtt a bájtokat átadta volna a Utf8JsonReaderfájlnak, mivel az olvasó szöveget vár. Ellenkező esetben a BOM érvénytelen JSON-nak minősül, és az olvasó kivételt jelez.

Íme egy JSON-minta, amelyet az előző kód képes olvasni. Az eredményként kapott összefoglaló üzenet a következő: "4-ből 2 neve "Egyetem" végződésű:

[
  {
    "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"
  }
]

Olvasás streamből a következő használatával: Utf8JsonReader

Nagy méretű (például gigabájt méretű) fájlok olvasása esetén érdemes lehet elkerülni, hogy a teljes fájlt egyszerre töltse be a memóriába. Ebben a forgatókönyvben használhat egy FileStream.

Utf8JsonReader Streamből való olvasáskor a következő szabályok érvényesek:

  • A részleges JSON hasznos adatokat tartalmazó puffernek legalább olyan nagynak kell lennie, mint a legnagyobb JSON-jogkivonat, hogy az olvasó előrehaladjon.
  • A puffernek legalább akkora méretűnek kell lennie, mint a legnagyobb szabad terület a JSON-ban.
  • Az olvasó nem követi nyomon az általa beolvasott adatokat, amíg teljesen be nem olvassa a következőt TokenType a JSON hasznos adatai között. Így ha a pufferben bájtok vannak hátra, újra át kell adnia őket az olvasónak. Megadhatja BytesConsumed , hogy hány bájt maradjon hátra.

Az alábbi kód bemutatja, hogyan olvashat egy streamből. A példa egy MemoryStream. A hasonló kód egy FileStreamUTF-8-ás anyagjegyzéket tartalmaz az elején, kivéve, ha az FileStream UTF-8 BOM-ot tartalmaz. Ebben az esetben a fennmaradó bájtok továbbítása előtt a pufferből le kell csíkolnia Utf8JsonReaderezt a három bájtot. Ellenkező esetben az olvasó kivételt vetne ki, mivel a BOM nem tekinthető a JSON érvényes részének.

A mintakód egy 4 KB-os pufferrel kezdődik, és minden alkalommal megduplázza a pufferméretet, amikor megállapítja, hogy a méret nem elég nagy ahhoz, hogy elférjen egy teljes JSON-jogkivonatban, ami ahhoz szükséges, hogy az olvasó előrehaladjon a JSON hasznos adatain. A kódrészletben megadott JSON-minta csak akkor aktiválja a pufferméret növelését, ha nagyon kis kezdeti pufferméretet állít be, például 10 bájtot. Ha a kezdeti pufferméretet 10-esre állítja, az utasítások a Console.WriteLine pufferméret növekedésének okát és hatását szemléltetik. A 4 KB kezdeti pufferméretnél a teljes JSON-minta látható mindegyiknél Console.WriteLine, és a pufferméretet soha nem kell növelni.

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

Az előző példa nem állít be korlátot a puffer méretének növekedésére. Ha a jogkivonat mérete túl nagy, a kód kivétellel OutOfMemoryException meghiúsulhat. Ez akkor fordulhat elő, ha a JSON legalább 1 GB méretű jogkivonatot tartalmaz, mivel az 1 GB-os méret megduplázása olyan méretet eredményez, amely túl nagy ahhoz, hogy beleférjen egy int32 pufferbe.

ref struct korlátozások

Mivel a Utf8JsonReader típus ref struct, bizonyos korlátozásokkal rendelkezik. Például nem tárolható mezőként egy osztályban, és nem lehet más struktúraként tárolni, mint egy átfstruktúra.

A nagy teljesítmény eléréséhez ennek a típusnak egy olyan típusnak kell lennie ref struct , mivel gyorsítótárazza a bemeneti ReadOnlySpan<bájtot>, amely maga is egy refstruct. Emellett a Utf8JsonReader típus nem módosítható, mivel állapotot tartalmaz. Ezért ne érték, hanem hivatkozás útján adja át. Ha érték szerint adja át, az strukturált példányt eredményez, és az állapotváltozások nem lesznek láthatók a hívó számára.

Az újrafstrukturálások használatáról további információt a foglalások elkerülése című témakörben talál.

UTF-8 szöveg olvasása

Ha a lehető legjobb teljesítményt szeretné elérni használat Utf8JsonReaderközben, olvassa el az UTF-16 sztringek helyett már UTF-8 szövegként kódolt JSON-hasznos adatokat. Példakód : Adatok szűrése az Utf8JsonReader használatával.

Olvasás többszegmensű ReadOnlySequence használatával

Ha a JSON-bemenet egy ReadOnlySpan<bájt>, minden JSON-elem elérhető az ValueSpan olvasó tulajdonságából, miközben végighalad az olvasási cikluson. Ha azonban a bemenet egy ReadOnlySequence bájt<> (amely egy PipeReaderolvasás eredménye), egyes JSON-elemek az objektum több szegmensét ReadOnlySequence<byte> is áttehetik. Ezek az elemek nem érhetők el egy összefüggő memóriablokkból ValueSpan . Ehelyett, ha több szegmenst ReadOnlySequence<byte> használ bemenetként, lekérdezi az HasValueSequence olvasó tulajdonságát, hogy megtudja, hogyan érheti el az aktuális JSON-elemet. Íme egy ajánlott minta:

while (reader.Read())
{
    switch (reader.TokenType)
    {
        // ...
        ReadOnlySpan<byte> jsonElement = reader.HasValueSequence ?
            reader.ValueSequence.ToArray() :
            reader.ValueSpan;
        // ...
    }
}

ValueTextEquals használata tulajdonságnév-keresésekhez

Ne használjon ValueSpan bájtonkénti összehasonlítást a tulajdonságnév-keresések meghívásával SequenceEqual . Hívjon ValueTextEquals inkább, mert ez a metódus felfedi a JSON-ban megszabadult karaktereket. Íme egy példa, amely bemutatja, hogyan kereshet egy "name" nevű tulajdonságot:

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;
    }
}

Null értékek beolvasása null értékű értéktípusokba

A beépített System.Text.Json API-k csak nem null értékű értéktípusokat ad vissza. Például egy Utf8JsonReader.GetBooleanbool. Kivételt eredményez, ha a JSON-ban talál Null . Az alábbi példák két módszert mutatnak be a null értékek kezelésére, az egyik a null értékű típus visszaadásával, a másik pedig az alapértelmezett érték visszaadásával:

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();
}

Token gyermekeinek kihagyása

Utf8JsonReader.Skip() A metódus használatával kihagyhatja az aktuális JSON-jogkivonat gyermekeit. Ha a jogkivonat típusa az JsonTokenType.PropertyName, az olvasó a tulajdonságértékre lép. Az alábbi kódrészlet egy példát Utf8JsonReader.Skip() mutat be arra, hogy az olvasót egy tulajdonság értékére kell áthelyezni.

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;
    }
}

Dekódolt JSON-sztringek felhasználása

A .NET 7-től kezdve használhatja a metódust ahelyett Utf8JsonReader.CopyStringUtf8JsonReader.GetString() , hogy dekódolt JSON-sztringet használ. Ellentétben GetString()az új sztringgel, amely mindig lefoglal egy új sztringet, CopyString lehetővé teszi a le nem ágyazott sztring másolását egy saját pufferbe. Az alábbi kódrészlet egy UTF-16 sztring CopyStringhasználatát szemlélteti.

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)
{
    // ...
}

Lásd még