Utf8JsonReader'ı kullanma System.Text.Json

Bu makalede, özel ayrıştırıcılar ve seri durumdan Utf8JsonReader çıkarıcılar oluşturmak için türünü nasıl kullanabileceğiniz gösterilmektedir.

Utf8JsonReader utf-8 ile kodlanmış JSON metni için yüksek performanslı, düşük ayırmalı, yalnızca ileriye dönük okuyucudur ve bir ReadOnlySpan<byte> veya ReadOnlySequence<byte>dosyasından okur. Utf8JsonReader, özel ayrıştırıcılar ve seri durumdan çıkarıcılar oluşturmak için kullanılabilecek düşük düzeyli bir türüdür. JsonSerializer.Deserialize Yöntemler, kapakların altında kullanılırUtf8JsonReader.

Utf8JsonReader doğrudan Visual Basic kodundan kullanılamaz. Daha fazla bilgi için bkz . Visual Basic desteği.

Aşağıdaki örnekte sınıfın nasıl kullanılacağı gösterilmektedir Utf8JsonReader :

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://video2.skills-academy.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support

Yukarıdaki kod, değişkenin jsonUtf8 UTF-8 olarak kodlanmış geçerli JSON içeren bir bayt dizisi olduğunu varsayar.

Kullanarak verileri filtreleme Utf8JsonReader

Aşağıdaki örnekte, bir dosyanın zaman uyumlu bir şekilde nasıl okunduğu ve bir değerin nasıl aranduğu gösterilmektedir.

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://video2.skills-academy.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support

Bu örneğin zaman uyumsuz sürümü için bkz . .NET örnekleri JSON projesi.

Yukarıdaki kod:

  • JSON'un bir nesne dizisi içerdiğini ve her nesnenin dize türünde bir "name" özelliği içerebileceğini varsayar.

  • "Üniversite" ile biten nesneleri ve "name" özellik değerlerini sayar.

  • Dosyanın UTF-16 olarak kodlanmış olduğunu varsayar ve dosyayı UTF-8'e çevirir. UTF-8 olarak kodlanmış bir dosya, aşağıdaki kod kullanılarak doğrudan içinde ReadOnlySpan<byte> okunabilir:

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

    Dosya bir UTF-8 bayt sipariş işareti (BOM) içeriyorsa, okuyucu metin beklediğinden baytları öğesine Utf8JsonReadergeçirmeden önce dosyayı kaldırın. Aksi takdirde, BOM geçersiz JSON olarak kabul edilir ve okuyucu bir özel durum oluşturur.

Yukarıdaki kodun okuyabileceği bir JSON örneği aşağıda verilmiştır. Sonuçta elde edilen özet ileti "4'ün 2'sinde "Üniversite" ile biten adlar var" şeklindedir:

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

Kullanarak bir akıştan okuma Utf8JsonReader

Büyük bir dosyayı (örneğin, gigabayt veya daha büyük boyutlu) okurken, dosyanın tamamını aynı anda belleğe yüklemek zorunda kalmamak isteyebilirsiniz. Bu senaryo için bir FileStreamkullanabilirsiniz.

bir akıştan okumak için kullanırken Utf8JsonReader aşağıdaki kurallar geçerlidir:

  • Okuyucunun ilerleme kaydedebilmesi için kısmi JSON yükünü içeren arabellek en az içindeki en büyük JSON belirteci kadar büyük olmalıdır.
  • Arabellek en az JSON içindeki en büyük boşluk dizisi kadar büyük olmalıdır.
  • Okuyucu, JSON yükünde bir sonrakini TokenType tamamen okuyana kadar okuduğu verileri izlemez. Bu nedenle, arabellekte kalan baytlar olduğunda, bunları okuyucuya yeniden geçirmeniz gerekir. Kaç bayt kaldığını belirlemek için kullanabilirsiniz BytesConsumed .

Aşağıdaki kodda bir akıştan nasıl okunduğu gösterilmektedir. Örnekte bir MemoryStreamgösterilir. Başlangıçta UTF-8 ürün reçetesi içermesi FileStream dışında benzer kod ile FileStreamçalışır. Bu durumda, kalan baytları öğesine geçirmeden önce bu üç baytı Utf8JsonReaderarabellekten ayırmanız gerekir. Aksi takdirde, ürün reçetesi JSON'un geçerli bir parçası olarak kabul edilmediğinden okuyucu bir özel durum oluşturur.

Örnek kod 4 KB arabellekle başlar ve boyutun tam bir JSON belirtecine sığacak kadar büyük olmadığını her bulduğunda arabellek boyutunu iki katına çıkartır. Bu, okuyucunun JSON yükünde ileriye doğru ilerlemesi için gereklidir. Kod parçacığında sağlanan JSON örneği, yalnızca 10 bayt gibi çok küçük bir başlangıç arabellek boyutu ayarladığınızda arabellek boyutu artışı tetikler. İlk arabellek boyutunu 10 olarak ayarlarsanız, Console.WriteLine deyimler arabellek boyutu artışlarının nedenini ve etkisini gösterir. 4 KB ilk arabellek boyutunda, örnek JSON'un tamamı her Console.WriteLinebiri tarafından gösterilir ve arabellek boyutunun hiçbir zaman artırılması gerekmez.

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://video2.skills-academy.com/dotnet/standard/serialization/system-text-json-how-to#visual-basic-support

Yukarıdaki örnek, arabelleğin ne kadar büyüyebileceğiyle ilgili bir sınır ayarlamaz. Belirteç boyutu çok büyükse kod bir OutOfMemoryException özel durumla başarısız olabilir. JSON boyutu yaklaşık 1 GB veya daha büyük bir belirteç içeriyorsa bu durum oluşabilir çünkü 1 GB boyutu ikiye katlama, arabelleğe sığamayacak kadar büyük bir int32 boyuta neden olur.

başvuru yapısı sınırlamaları

Utf8JsonReader Türü bir başvuru yapısı olduğundan, belirli sınırlamaları vardır. Örneğin, bir sınıf veya yapıda başvuru yapısı dışında bir alan olarak depolanamaz.

Yüksek performans elde etmek için bu tür bir ref struct olmalıdır çünkü kendisi bir başvuru yapısı olan ReadOnlySpan<bayt> girişini önbelleğe alması gerekir. Buna ek olarak, Utf8JsonReader durum barındırdığı için türü değişebilir. Bu nedenle, değere göre değil başvuruya göre geçirin. Değere göre geçirilmesi bir yapı kopyasına neden olur ve durum değişiklikleri çağıran tarafından görünmez.

Başvuru yapılarını kullanma hakkında daha fazla bilgi için bkz . Ayırmalardan kaçınma.

UTF-8 metnini okuma

kullanırken Utf8JsonReadermümkün olan en iyi performansı elde etmek için, UTF-16 dizeleri yerine zaten UTF-8 metni olarak kodlanmış JSON yüklerini okuyun. Kod örneği için bkz . Utf8JsonReader kullanarak verileri filtreleme.

Çok segmentli ReadOnlySequence ile okuma

JSON girişiniz bir ReadOnlySpan<bayt> ise, okuma döngüsü boyunca her JSON öğesine okuyucudaki özelliğinden ValueSpan erişilebilir. Ancak, girişiniz bir ReadOnlySequence<bayt> ise (bu, bir PipeReader'den okumanın sonucudur), bazı JSON öğeleri nesnenin birden çok kesimine ReadOnlySequence<byte> karışabilir. Bu öğelere bitişik bir bellek bloğundan erişilemez ValueSpan . Bunun yerine, giriş olarak çok segmentli ReadOnlySequence<byte> bir öğeniz olduğunda, geçerli JSON öğesine nasıl erişeceklerini öğrenmek için okuyucuda özelliğini yoklayın HasValueSequence . Önerilen desen aşağıdadır:

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

Özellik adı aramaları için ValueTextEquals kullanma

Özellik adı aramalarını çağırarak SequenceEqual bayt bayt karşılaştırmaları yapmak için kullanmayınValueSpan. Bunun yerine çağırın ValueTextEquals çünkü bu yöntem, JSON'da kaçış yapılan tüm karakterlerin çıkışını çıkarır. "name" adlı bir özelliğin nasıl arandığını gösteren bir örnek aşağıda verilmiştir:

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 değerleri null değer türlerine okuma

Yerleşik API'ler System.Text.Json yalnızca null atanamayan değer türleri döndürür. Örneğin, Utf8JsonReader.GetBoolean bir booldöndürür. JSON içinde bulursa Null bir özel durum oluşturur. Aşağıdaki örneklerde, biri null değer türü döndürerek, diğeri varsayılan değeri döndürerek olmak üzere null değerleri işlemenin iki yolu gösterilmektedir:

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

Belirtecin alt öğelerini atla

Utf8JsonReader.Skip() Geçerli JSON belirtecinin alt öğelerini atlamak için yöntemini kullanın. Belirteç türü ise JsonTokenType.PropertyName, okuyucu özellik değerine geçer. Aşağıdaki kod parçacığında, okuyucuyu bir özelliğin değerine taşımak için kullanma Utf8JsonReader.Skip() örneği gösterilmektedir.

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

Kodu çözülen JSON dizelerini kullanma

.NET 7'den başlayarak, kodu çözülen bir JSON dizesi kullanmak yerine Utf8JsonReader.GetString() yöntemini kullanabilirsinizUtf8JsonReader.CopyString. sürümünden farklı olarak GetString(), her zaman yeni bir dize ayırır, CopyString boş dizeyi sahip olduğunuz bir arabelleğe kopyalamanıza olanak tanır. Aşağıdaki kod parçacığında kullanarak CopyStringUTF-16 dizesi kullanma örneği gösterilmektedir.

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

Ayrıca bkz.