C# JSON İşlemleri: System.Text.Json ile Serileştirme ve Deserileştirme

Unity/C# projelerinizde System.Text.Json kütüphanesini kullanarak veri serileştirme ve deserileştirme işlemlerini öğrenin. Temel kullanımdan gelişmiş ayarlara kadar her şey bu makalede.

Günümüz oyun geliştirme ve uygulama dünyasında veri alışverişi, özellikle de farklı sistemler arasında, büyük önem taşımaktadır. Bu alışverişin en popüler formatlarından biri şüphesiz JSON (JavaScript Object Notation) formatıdır. JSON, insan tarafından okunabilir ve makineler tarafından kolayca işlenebilir yapısıyla, API iletişiminden yerel veri depolamaya kadar birçok alanda tercih edilmektedir. C# geliştiricileri için JSON işlemleri, özellikle Unity gibi platformlarda, oyun içi verileri kaydetmek, sunucuyla iletişim kurmak veya harici konfigürasyon dosyalarını yönetmek için vazgeçilmez bir beceridir.

.NET ekosisteminde uzun yıllar boyunca Newtonsoft.Json (Json.NET) kütüphanesi standart olarak kabul edildi. Ancak .NET Core 3.0 ile birlikte Microsoft, kendi yerel, yüksek performanslı ve modern JSON kütüphanesini tanıttı: System.Text.Json. Bu makalede, Unity/C# projelerinizde System.Text.Json kütüphanesini kullanarak C# nesnelerini JSON formatına serileştirmeyi ve JSON formatındaki verileri C# nesnelerine deserileştirmeyi, temelden ileri seviyeye kadar tüm detaylarıyla inceleyeceğiz.

System.Text.Json Nedir ve Neden Önemlidir?

System.Text.Json, .NET Core 3.0 ve sonraki sürümlerle birlikte gelen, Microsoft tarafından geliştirilen bir JSON serileştirme/deserileştirme kütüphanesidir. Temel amacı, modern .NET uygulamaları için yüksek performanslı, güvenli ve standartlara uygun bir JSON işleme çözümü sunmaktır. Newtonsoft.Json‘a kıyasla bazı önemli avantajları vardır:

  • Performans: Genellikle daha hızlı ve daha az bellek kullanır, özellikle büyük veri setlerinde bu fark belirginleşir.
  • Güvenlik: Varsayılan olarak daha güvenli kabul edilir, çünkü daha katı kurallara sahiptir ve bazı potansiyel güvenlik açıklarını kapatır.
  • Entegrasyon: .NET ekosisteminin doğal bir parçasıdır ve .NET API’leriyle daha derin entegrasyona sahiptir.

Unity projelerinde System.Text.Json kullanmak için, projenizin .NET Standard 2.1 veya üstünü hedeflediğinden emin olmanız ve NuGet paket yöneticisi aracılığıyla System.Text.Json paketini eklemeniz gerekebilir. Çoğu modern Unity projesi bu gereksinimleri karşılar.

Temel Serileştirme (Serialization)

Serileştirme, bir C# nesnesini (örneğin, bir sınıfın veya yapının bir örneğini) JSON formatında bir metin dizesine dönüştürme işlemidir. Bu, nesnenin durumunu kaydetmek, ağ üzerinden göndermek veya bir dosyaya yazmak için kullanılır.

Örnek olarak, bir oyuncu verisini temsil eden basit bir C# sınıfı oluşturalım:


using System;
using System.Text.Json;

public class OyuncuVerisi
{
    public string OyuncuAdi { get; set; }
    public int Seviye { get; set; }
    public float DeneyimPuani { get; set; }
    public DateTime SonGirisTarihi { get; set; }
    public bool AktifMi { get; set; }
}

public class JsonOrnek
{
    public static void SerilestirmeOrnegi()
    {
        OyuncuVerisi oyuncu = new OyuncuVerisi
        {
            OyuncuAdi = "KaptanUnity",
            Seviye = 42,
            DeneyimPuani = 1234.5f,
            SonGirisTarihi = DateTime.Now,
            AktifMi = true
        };

        // Oyuncu nesnesini JSON formatına serileştiriyoruz.
        string jsonString = JsonSerializer.Serialize(oyuncu);
        Console.WriteLine("Serileştirilmiş JSON:" + jsonString);
        // Çıktı (tek satırda): {"OyuncuAdi":"KaptanUnity","Seviye":42,"DeneyimPuani":1234.5,"SonGirisTarihi":"2023-10-27T10:00:00Z","AktifMi":true}
    }
}

Temel Deserileştirme (Deserialization)

Deserileştirme, bir JSON metin dizesini alıp, bu dizeyi karşılık gelen C# nesnesinin bir örneğine dönüştürme işlemidir. Bu, kaydedilmiş verileri yüklemek veya bir API’den gelen yanıtı işlemek için kullanılır.


using System;
using System.Text.Json;

public class JsonOrnek
{
    public static void DeserilestirmeOrnegi()
    {
        string jsonString = "{\"OyuncuAdi\":\"KaptanUnity\",\"Seviye\":42,\"DeneyimPuani\":1234.5,\"SonGirisTarihi\":\"2023-10-27T10:00:00Z\",\"AktifMi\":true}";

        // JSON dizesini OyuncuVerisi nesnesine deserileştiriyoruz.
        OyuncuVerisi oyuncu = JsonSerializer.Deserialize<OyuncuVerisi>(jsonString);

        Console.WriteLine($"Deserileştirilmiş Oyuncu Adı: {oyuncu.OyuncuAdi}");
        Console.WriteLine($"Deserileştirilmiş Seviye: {oyuncu.Seviye}");
        Console.WriteLine($"Deserileştirilmiş Deneyim Puanı: {oyuncu.DeneyimPuani}");
        Console.WriteLine($"Deserileştirilmiş Son Giriş Tarihi: {oyuncu.SonGirisTarihi}");
        Console.WriteLine($"Deserileştirilmiş Aktif Mi: {oyuncu.AktifMi}");
    }
}

System.Text.Json’ın Gelişmiş Özellikleri

System.Text.Json, temel işlemlerin ötesinde, serileştirme ve deserileştirme sürecini özelleştirmek için zengin seçenekler sunar.

Özelleştirilmiş Serileştirme Ayarları (JsonSerializerOptions)

JsonSerializerOptions sınıfı, serileştirme ve deserileştirme davranışını kontrol etmek için kullanılır. Bu, JSON çıktısının biçimlendirilmesinden, özelliklerin nasıl işleneceğine kadar birçok ayarı içerir.


using System;
using System.Text.Json;

public class JsonOrnek
{
    public static void OzellestirilmisSerilestirmeOrnegi()
    {
        OyuncuVerisi oyuncu = new OyuncuVerisi
        {
            OyuncuAdi = "KaptanUnity",
            Seviye = 42,
            DeneyimPuani = 1234.5f,
            SonGirisTarihi = DateTime.Now,
            AktifMi = true
        };

        var options = new JsonSerializerOptions
        {
            WriteIndented = true, // JSON çıktısını okunabilir (girintili) hale getirir
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // Özellik isimlerini camelCase yapar
            AllowTrailingCommas = true, // JSON'da sonda virgül olmasına izin verir (deserileştirme için)
            IgnoreReadOnlyProperties = false // Sadece getter'ı olan property'leri dahil eder
        };

        string jsonString = JsonSerializer.Serialize(oyuncu, options);
        Console.WriteLine("Özelleştirilmiş Serileştirilmiş JSON:\n" + jsonString);
        /* Çıktı:
        {
          "oyuncuAdi": "KaptanUnity",
          "seviye": 42,
          "deneyimPuani": 1234.5,
          "sonGirisTarihi": "2023-10-27T10:00:00Z",
          "aktifMi": true
        }
        */
    }
}

JSON Niteliklerinin (Attributes) Kullanımı

C# sınıflarınızdaki property’ler üzerine yerleştireceğiniz nitelikler (attributes) ile System.Text.Json‘ın davranışını doğrudan kontrol edebilirsiniz. Bu, daha esnek ve okunabilir bir kod sağlar.

  • [JsonPropertyName("yeniIsim")]: Bir özelliğin JSON çıktısındaki adını değiştirmek için kullanılır.
  • [JsonIgnore]: Bir özelliğin serileştirme ve deserileştirme sürecine dahil edilmemesini sağlar.
  • [JsonInclude]: Varsayılan olarak serileştirilmeyen (örneğin, private veya internal setter’ı olan) bir özelliği serileştirmeye zorlar.
  • [JsonNumberHandling]: Sayıların nasıl işleneceğini kontrol eder (örneğin, string olarak okunup yazılması).
  • [JsonConverter(typeof(MyCustomConverter))]: Özel bir dönüştürücü (converter) belirtmek için kullanılır.

Örnek:


using System.Text.Json.Serialization;

public class GelişmişOyuncuVerisi
{
    [JsonPropertyName("player_name")] // JSON'da "player_name" olarak görünecek
    public string OyuncuAdi { get; set; }

    public int Seviye { get; set; }

    [JsonIgnore] // Bu özellik JSON'a dahil edilmeyecek
    public string GizliBilgi { get; set; }

    private float _deneyimPuani;

    [JsonInclude] // private bir field olmasına rağmen serileştirilecek
    public float DeneyimPuani
    {
        get => _deneyimPuani;
        set => _deneyimPuani = value;
    }

    public GelişmişOyuncuVerisi()
    {
        GizliBilgi = "Çok Gizli!";
        _deneyimPuani = 0;
    }
}

public class JsonOrnek
{
    public static void AttributeSerilestirmeOrnegi()
    {
        GelişmişOyuncuVerisi oyuncu = new GelişmişOyuncuVerisi
        {
            OyuncuAdi = "UnityPro",
            Seviye = 50,
            DeneyimPuani = 2500.75f
        };

        var options = new JsonSerializerOptions { WriteIndented = true };
        string jsonString = JsonSerializer.Serialize(oyuncu, options);
        Console.WriteLine("Attribute ile Serileştirilmiş JSON:\n" + jsonString);
        /* Çıktı:
        {
          "player_name": "UnityPro",
          "Seviye": 50,
          "DeneyimPuani": 2500.75
        }
        */
    }
}

Pratik İpuçları

1. Tarih/Saat Formatlarını Yönetmek

DateTime nesneleri serileştirilirken genellikle ISO 8601 formatında (örn: "2023-10-27T10:00:00Z") string’e dönüştürülür. Farklı bir formata ihtiyacınız varsa, özel bir JsonConverter yazabilir veya JsonSerializerOptions içinde DefaultDateTimeOffsetHandling gibi ayarları kullanabilirsiniz. Ancak genellikle varsayılan format yeterlidir ve en iyi uyumluluğu sağlar.

2. İsimlendirme Kurallarını (Naming Policies) Kullanmak

API’ler genellikle camelCase veya snake_case gibi farklı isimlendirme kuralları kullanır. C# kodunuzdaki PascalCase isimleri bu kurallara otomatik olarak dönüştürmek için JsonNamingPolicy kullanabilirsiniz:


var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase // C# property'lerini camelCase yapar
};
// Deserileştirme yaparken de aynı policy'yi kullanmalısınız.

3. Performans İçin JsonSerializerOptions Örneğini Yeniden Kullanmak

Eğer uygulamanızda sık sık JsonSerializerOptions nesnesiyle serileştirme/deserileştirme yapıyorsanız, her seferinde yeni bir options nesnesi oluşturmak yerine, tek bir örneği (örneğin, static readonly bir alan olarak) tanımlayıp yeniden kullanmak performansı artıracaktır. JsonSerializerOptions thread-safe’dir, bu yüzden birden fazla thread’den güvenle erişilebilir.


public static class JsonAyarlar
{
    public static readonly JsonSerializerOptions VarsayilanAyarlar = new JsonSerializerOptions
    {
        WriteIndented = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        AllowTrailingCommas = true
    };
}

// Kullanım:
string jsonString = JsonSerializer.Serialize(oyuncu, JsonAyarlar.VarsayilanAyarlar);

Yaygın Hatalar ve Çözümleri

1. public Olmayan Property’ler Serileştirilmiyor

Hata: System.Text.Json varsayılan olarak yalnızca public erişim belirleyicisine sahip ve hem get hem de set metotları olan property’leri serileştirir. Eğer bir property private, internal ise veya sadece get metodu varsa, JSON çıktısına dahil edilmez.

Çözüm: Property’yi public yapın veya sadece getter’ı olan property’ler için [JsonInclude] niteliğini kullanın. Eğer bir field’ı serileştirmek isterseniz, onu da [JsonInclude] ile işaretlemelisiniz.

2. Tarih Formatı Uyuşmazlığı

Hata: JSON dizesindeki tarih formatı, C# DateTime veya DateTimeOffset nesnesinin beklediği formata uymadığında deserileştirme hatası alınabilir.

Çözüm: JSON verisinin ISO 8601 formatında olduğundan emin olun. Eğer farklı bir format kullanılıyorsa, özel bir JsonConverter yazarak veya JsonSerializerOptions içinde uygun ayarları (ancak System.Text.Json‘da doğrudan format string belirtme imkanı Newtonsoft.Json kadar esnek değildir) kullanarak bu formatı işleyebilirsiniz. Genellikle en iyi pratik, tüm sistemlerinizde standart bir tarih formatı kullanmaktır.

3. Enum’ların Sayısal Değer Olarak Serileştirilmesi

Hata: Varsayılan olarak, enum’lar JSON’a sayısal değerleri olarak serileştirilir (örn: "Durum": 0). Ancak genellikle enum isimlerinin (örn: "Durum": "Hazir") serileştirilmesi istenir.

Çözüm: JsonSerializerOptions nesnesine JsonStringEnumConverter ekleyin veya enum property’si üzerine [JsonConverter(typeof(JsonStringEnumConverter))] niteliğini uygulayın.


using System.Text.Json.Serialization;

public enum OyunDurumu { Baslamadi, Oynaniyor, Tamamlandi }

public class OyunVerisi
{
    public string OyunAdi { get; set; }
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public OyunDurumu Durum { get; set; }
}

public class JsonOrnek
{
    public static void EnumSerilestirmeOrnegi()
    {
        OyunVerisi oyun = new OyunVerisi { OyunAdi = "Harika Oyun", Durum = OyunDurumu.Oynaniyor };
        
        var options = new JsonSerializerOptions
        {
            WriteIndented = true,
            // Converters.Add(new JsonStringEnumConverter()); // Global olarak eklemek için
        };

        string jsonString = JsonSerializer.Serialize(oyun, options);
        Console.WriteLine("Enum Serileştirilmiş JSON:\n" + jsonString);
        /* Çıktı:
        {
          "OyunAdi": "Harika Oyun",
          "Durum": "Oynaniyor"
        }
        */
    }
}

Performans ve Optimizasyon Notları

System.Text.Json zaten yüksek performanslı olacak şekilde tasarlanmıştır, ancak yine de dikkat etmeniz gereken bazı noktalar vardır:

  • JsonSerializerOptions Yeniden Kullanımı: Yukarıda belirtildiği gibi, JsonSerializerOptions nesnelerini tekil (singleton) olarak tanımlamak ve yeniden kullanmak, her serileştirme/deserileştirme çağrısında yeni bir nesne oluşturmanın getireceği ek yükü ortadan kaldırır.
  • Akış Tabanlı İşlemler (Stream-based Operations): Çok büyük JSON dosyaları veya ağ akışları ile çalışırken, tüm veriyi belleğe tek seferde yüklemek yerine JsonSerializer.SerializeAsync ve JsonSerializer.DeserializeAsync gibi akış tabanlı metotları kullanmak bellek kullanımını önemli ölçüde azaltabilir ve performansı artırabilir.
  • Source Generators (Kaynak Üreteçleri): .NET 6 ve sonraki sürümlerle birlikte gelen System.Text.Json kaynak üreteçleri, çalışma zamanında yansıma (reflection) kullanmak yerine derleme zamanında serileştirme/deserileştirme kodu oluşturarak performansı daha da ileriye taşır. Özellikle Unity’de IL2CPP derlemesi yapılırken reflection bazlı çözümler performans sorunları yaratabilir. Kaynak üreteçleri bu sorunu büyük ölçüde hafifletir. Bu konu ileri seviye bir konu olmakla birlikte, bilmek faydalıdır.

Sonuç

System.Text.Json, modern C# ve Unity projelerinde JSON verileriyle çalışmak için güçlü, esnek ve performanslı bir çözümdür. Bu makalede ele aldığımız temel serileştirme ve deserileştirme işlemlerinden, JsonSerializerOptions ile özelleştirmelere ve nitelik (attribute) kullanımına kadar birçok konuyu öğrenerek, veri yönetimi becerilerinizi geliştirebilirsiniz. Unutmayın, doğru araçları doğru şekilde kullanmak, uygulamanızın performansını ve sürdürülebilirliğini doğrudan etkiler.

Unity geliştiricileri olarak, oyun içi kayıt sistemlerinden, uzak sunucularla veri alışverişine kadar birçok senaryoda System.Text.Json‘dan faydalanabilirsiniz. Şimdi bu bilgileri kendi projelerinizde deneme zamanı!

Leave a Reply

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir