System.Text.Json · Custom Converter

Aynı property ismini bin kere yazmayı bırakın.

TabularJsonConverterFactory, POCO koleksiyonlarını her satırda aynı alan adlarını tekrar tekrar yazan standart JSON yerine, tek bir header satırı ve altında sade veri satırları olan tabular JSON formatına serialize / deserialize eden, System.Text.Json üzerine kurulu hafif bir converter kütüphanesidir.

.NET uyumlu C# System.Text.Json native MIT lisans Harici bağımlılık yok
Standart JSON
people.json
[
  { "Id": 1, "Name": "Ethem", "Age": 35 },
  { "Id": 2, "Name": "Ahmet", "Age": 28 },
  { "Id": 3, "Name": "Mehmet", "Age": 42 }
]
Tabular JSON
people.tabular.json
[
  [ "Id", "Name", "Age" ],
  [ 1, "Ethem", 35 ],
  [ 2, "Ahmet", 28 ],
  [ 3, "Mehmet", 42 ]
]
01 · Genel Bakış

Bu kütüphane ne işe yarar?

Aynı tipten çok sayıda kayıt taşıyan koleksiyonları JSON'a çevirirken, her kaydın kendi property isimlerini tekrar tekrar yazması israftır. TabularJsonConverterFactory bu tekrarı, isimleri bir kez tanımlayan bir header ve altındaki saf veri satırlarına ayırarak ortadan kaldırır — üstelik round-trip, yani hem serialize hem deserialize yönünde tam destekle.

Object Collection (List<T>, T[]…)
Tabular JSON
Daha kompakt veri yapısı

Kütüphane; büyük koleksiyonlar, API response'ları, veri export/import işlemleri, log/event payload'ları, JSON tabanlı cache ve dosya tabanlı veri aktarımı gibi machine-to-machine senaryolarda değerlendirilmek üzere tasarlanmıştır. Bir sıkıştırma algoritması değildir; JSON'un yapısal tekrarını azaltan alternatif bir serialization formatıdır.

02 · Gerekçe

Neden tabular JSON?

1.000 kayıtlık bir koleksiyonda her satır aynı 4-5 property ismini yeniden yazar. Kayıt sayısı ve alan adı uzunluğu arttıkça bu tekrarın payload üzerindeki etkisi de büyür. Aşağıdaki canlı hesaplayıcı bunu somutlaştırıyor.

Kayıt sayısı (satır) 500
Standart JSON
— KB
Tabular JSON
— KB
daha az payload (yaklaşık)

Hesaplama; musteri_id, musteri_adi, tutar, para_birimi, olusturma_tarihi şeklinde 5 kolonluk örnek bir API kaydı üzerinden yaklaşık karakter sayımıyla yapılmıştır. Gerçek kazanım, veri yapısına, alan adı uzunluğuna ve değerlerin boyutuna göre değişir — kütüphane bir sıkıştırma garantisi vermez, gerçek sonuçlar veri setine göre ölçülmelidir.

03 · Formatlar

İki farklı tabular temsil

İhtiyacınıza göre iki JSON şeklinden birini seçebilirsiniz.

ArrayOfArrays — en kompakt format
[
  [ "Id", "Name", "Age" ],
  [ 1, "Ethem", 35 ],
  [ 2, "Ahmet", 28 ],
  [ 3, "Mehmet", 42 ]
]
  • En kısa JSON çıktısı, en az property-name tekrarı
  • Tabular veri için doğal, CSV benzeri düşünce modeli
  • Export senaryoları için uygun
HeaderRows — daha açıklayıcı format
{
  "header": [ "Id", "Name", "Age" ],
  "rows": [
    [ 1, "Ethem", 35 ],
    [ 2, "Ahmet", 28 ],
    [ 3, "Mehmet", 42 ]
  ]
}
  • Header ve data JSON içinde açıkça ayrılır, daha okunabilir
  • API response'larında daha anlaşılır, debug sırasında daha rahat incelenir
  • İleride metadata eklemeye daha uygun bir yapı
04 · Özellikler

Kapsamlı, tek başına yeterli bir converter

Harici bir JSON framework'e ihtiyaç duymadan, mevcut System.Text.Json pipeline'ınıza doğrudan eklenir.

Round-tripSerialize ve deserialize yönünde tam destek.
JsonConverterFactoryHedef collection tipini runtime'da algılar.
Geniş collection desteğiT[], List<T>, IEnumerable<T>, IReadOnlyList<T>, ICollection<T>, IList<T>.
JsonPropertyNameMevcut naming policy'nize saygı gösterir.
Case-insensitive header"Name", "name", "NAME" aynı property'e eşleşir.
Class + structValue type'lar için ayrı, doğru ref setter mekanizması.
Nullable value typeint?, DateTime? gibi tipleri güvenle işler.
Eksik/fazla kolon toleransıŞema farklılıklarında satırı olabildiğince işler.
Bilinmeyen kolonları yok saymaPOCO'da karşılığı olmayan header'lar sessizce atlanır.
Expression compiled getter/setterTekrarlı reflection maliyetini ortadan kaldırır.
Metadata & header index cacheProperty bilgisi tip başına yalnızca bir kez üretilir.
Constructor fallbackParameterless constructor yoksa FormatterServices ile devam eder.
05 · Kurulum ve Kullanım

Dört adımda entegrasyon

Converter'ı kaydedin, modelinizi tanımlayın, serialize/deserialize edin.

1

Converter'ı kaydedin

JsonConverterFactory olarak tasarlandığı için mevcut JsonSerializerOptions.Converters koleksiyonuna eklenir.

var options = new JsonSerializerOptions();

options.Converters.Add(
    new TabularJsonConverterFactory(TabularFormat.ArrayOfArrays));
2

Modelinizi tanımlayın

Public, okunabilir/yazılabilir property'ler otomatik olarak kolonlara dönüşür.

public sealed class Person
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public int Age { get; set; }
}
3

Serialize edin

var people = new List<Person>
{
    new() { Id = 1, Name = "Ethem", Age = 35 },
    new() { Id = 2, Name = "Ahmet", Age = 28 }
};

var json = JsonSerializer.Serialize(people, options);
// [["Id","Name","Age"],[1,"Ethem",35],[2,"Ahmet",28]]
4

Deserialize edin

var people = JsonSerializer.Deserialize<List<Person>>(json, options);
// people[0].Name == "Ethem"

JsonPropertyName ile

Mevcut [JsonPropertyName("customer_id")] attribute'ları header isimlerine yansır; hem JSON adı hem property adı eşleşme anahtarı olarak kullanılır.

Struct desteği

Value type'lar için ref tabanlı ayrı bir setter mekanizması kullanılır, böylece value semantics doğru şekilde korunur.

JSON validasyonu

Beklenmeyen bir kök yapı, eksik header/rows alanı ya da array olmayan bir satır geldiğinde standart JsonException fırlatılır.

06 · Performans Mimarisi

Reflection bir kez, sonrası derlenmiş delegate

Her property erişiminde PropertyInfo.GetValue/SetValue çağırmak yerine, metadata oluşturulurken expression tree'ler compile edilir ve tekrarlı erişimlerde hazır delegate'ler kullanılır.

PropertyInfo │ ▼ Expression Tree │ ▼ Compile() │ ▼ Func<T, object?> / Action<T, object?> (getter / setter) │ ▼ Serialization & deserialization sırasında tekrar tekrar kullanılır

Metadata cache

Property metadata her işlemde yeniden üretilmez, tip başına statik generic alanlarda saklanır.

Header → index cache

Header isimlerinin property index'ine eşlenmesi önceden hazırlanır; her hücrede yeniden reflection araması yapılmaz.

Utf8JsonWriter

Serialization doğrudan Utf8JsonWriter üzerinden yapılır — System.Text.Json mimarisinin doğal genişleme noktası.

07 · Karşılaştırma

Standart JSON'a karşı

ÖzellikStandart Object JSONTabularJsonConverterFactory
Property adı her satırda tekrar ederEvetHayır
Header ayrı tutulabilirHayırEvet
Kompakt representation◼︎★★★★★
İnsan tarafından okunabilirlik★★★★★
Tabular veri için uygunluk★★★★★★★
POCO round-tripEvetEvet
System.Text.Json uyumuEvetEvet

ArrayOfArrays vs HeaderRows

ÖzellikArrayOfArraysHeaderRows
Kompaktlık★★★★★★★★★
Okunabilirlik★★★★★★★★
API response için uygunluk★★★★★★★★★
Debug kolaylığı★★★★★★★★
Metadata eklemeye uygunluk★★★★★★★★
08 · Ne Zaman Kullanmalı

Doğru araç, doğru senaryo

Tabular JSON her durumda standart JSON'dan üstün değildir; asıl amacı standart JSON'un yerini almak değil, uygun senaryolarda alternatif sunmaktır.

✅ Uygun olduğu durumlar

  • Çok sayıda aynı tipte kayıt taşıyan büyük koleksiyonlar
  • Property isimlerinin sürekli tekrarını istemediğiniz API payload'ları
  • SQL / DataTable benzeri tabular veriyi JSON olarak taşıma (database export)
  • Aynı tipte çok sayıda kaydı tek payload'da taşıyan batch processing
  • Tekrarlı JSON metadata'sını azaltmak istediğiniz cache senaryoları
  • Producer/consumer aynı kolon şemasında anlaştığı data interchange

⚠️ Uygun olmadığı durumlar

  • 3-5 kayıt gibi çok küçük koleksiyonlar — okunabilir standart JSON daha avantajlı
  • JSON'un doğrudan insan tarafından okunması önemli olan public API'ler
  • Self-describing (her satırın kendi alan adını taşıdığı) yapı beklenen entegrasyonlar
  • Kesin bir sıkıştırma garantisi aranan senaryolar — bu bir sıkıştırma algoritması değildir
REST API Internal API Microservice communication Database export Data import/export Cache serialization Batch processing ETL pipeline Reporting Large collection transport