Go 1.25 encoding/json/v2 davranış değişiklikleri ve v1 uyumluluk katmanının analizi

Go'nun encoding/json paketi v2, mevcut v1'in çeşitli dezavantajlarını (tutarsızlık, şaşırtıcı davranışlar, performans sorunları) gidermek için geliştirilmiş yeni bir uygulamadır. Bu özellik, goexperiment.jsonv2 derleme etiketi (build tag) aracılığıyla etkinleştirilen deneysel bir özelliktir.
En önemli nokta, v2 etkinleştirildiğinde v1'in, v2 uygulaması üzerinde v1'in davranışını taklit eden bir uyumluluk katmanı olarak çalışmasıdır. Bu, v2_v1.go dosyasındaki DefaultOptionsV1() fonksiyonu aracılığıyla gerçekleştirilir. Yani v2, v1'in davranışını tam olarak yeniden üretebilen seçenekler sunarken, aynı zamanda daha katı ve öngörülebilir yeni varsayılan davranışlar da önerir.
v2'nin başlıca hedefleri şunlardır:
- Doğruluk ve öngörülebilirliğin artırılması: Varsayılan olarak daha katı kurallar (ör. büyük/küçük harf duyarlılığı, yinelenen anahtarların yasaklanması) uygulayarak beklenmeyen davranışları azaltır.
- Performansın iyileştirilmesi: Ayrıştırma (parsing) ve kodlama (encoding) motorları, verimliliği artırmak için yeniden tasarlanmıştır.
- Esneklik ve denetimin genişletilmesi: Geliştiricilerin JSON işleme yöntemlerini ayrıntılı biçimde kontrol edebilmesi için kapsamlı bir seçenek sistemi sunulmuştur.
Başlıca Anlam/Davranış Farklılıkları
V1 ile v2 arasındaki davranış farklılıkları, v2_test.go dosyası temel alınarak madde madde özetlenmiştir.
1. Alan Adı Eşleştirme (Büyük/Küçük Harf Duyarlılığı)
- v1 davranışı: JSON nesne üyelerini Go yapı alanlarına unmarshal ederken büyük/küçük harfe duyarsız (case-insensitive) eşleştirme yapar. Hem
"FirstName"hem de"firstname",FirstNamealanına eşlenir. - v2 davranışı: Varsayılan olarak büyük/küçük harfe duyarlı (case-sensitive) çalışır ve yalnızca tam olarak eşleşen alanları eşler.
- Değişikliğin nedeni: Büyük/küçük harfe duyarsız eşleştirme, beklenmeyen davranışların kaynağı olabilir ve eşleşmeyen alanların işlenmesinde performans düşüşüne yol açar. v2, daha açık ve öngörülebilir davranışı varsayılan olarak benimsemiştir.
- İlgili seçenekler: v2'de,
json:"...,case:ignore"etiket seçeneği ile alan bazında büyük/küçük harf duyarsızlığı açıkça etkinleştirilebilir veyajson.MatchCaseInsensitiveNames(true)seçeneği genel olarak uygulanabilir.
2. omitempty Etiket Seçeneğinin Anlamındaki Değişiklik
- v1 davranışı: Alanları Go değerinin "boş (empty) durumuna" göre atlar. Burada "boş durum",
false,0,nilişaretçi/arayüz ve uzunluğu 0 olan dizi/dilim/harita/dizgi anlamına gelir. - v2 davranışı: Alanları kodlanmış JSON değerinin "boş durumuna" göre atlar. Yani
null,"",{}veya[]olarak kodlanan değerler atlanır. - Değişikliğin nedeni: v1'deki tanım Go'nun tür sistemine bağımlıdır. v2 ise JSON tür sistemini temel alarak daha tutarlı bir davranış sunar. Örneğin v1'de
booltüründekifalsedeğeri atlanırken, v2'defalseboş olmayan bir JSON değeri olduğu için atlanmaz. v2, v1'dekiomitempty'nin0veyafalsedeğerlerine uygulanan davranışının yerini alabilmesi içinomitzeroseçeneğini ekler. - İlgili seçenekler: v2'de v1 ile aynı davranışın istenmesi durumunda
json.OmitEmptyWithLegacyDefinition(true)seçeneği kullanılır.
3. string Etiket Seçeneğinin Davranış Değişikliği
- v1 davranışı: Sayı, mantıksal ve dizgi türündeki alanlara uygulanır. İlgili değeri bir JSON dizgisi içinde yeniden kodlar (ör.
int(42)->"42"). Bileşik türlerin (dilim, harita vb.) içindeki değerlere özyinelemeli olarak uygulanmaz. - v2 davranışı: Yalnızca sayısal türlere uygulanır ve özyinelemeli olarak işler. Yani
[]intgibi bir dilimin içindeki sayılar da JSON dizgisi olarak kodlanır. - Değişikliğin nedeni:
stringseçeneğinin temel amacı, 64 bitlik tam sayıların hassasiyet kaybını önlemek için sayıları dizgi olarak temsil etmektir. v1'in davranışı sınırlı ve tutarsızdı. v2, bu temel kullanıma odaklanmış ve davranışı özyinelemeli olarak genişleterek daha kullanışlı hale getirmiştir. - İlgili seçenekler: v1'in davranışını taklit etmek için
json.StringifyWithLegacySemantics(true)seçeneği kullanılabilir.
4. nil Dilim ve Harita Marshal İşlemi
- v1 davranışı:
nildilimler venilharitalarnullolarak marshal edilir. - v2 davranışı: Varsayılan olarak
nildilimler[](boş dizi),nilharitalar ise{}(boş nesne) olarak marshal edilir. - Değişikliğin nedeni:
nil, Go dilinin uygulama ayrıntısıdır ve dilden bağımsız bir JSON biçimine bunu yansıtmak uygun değildir. Boş koleksiyonları temsil eden[]ve{}daha yaygın bir gösterimdir. - İlgili seçenekler: v2'de
json.FormatNilSliceAsNull(true)veyajson.FormatNilMapAsNull(true)seçenekleriyle v1'deki gibinullolarak marshal edilebilir.
5. Dizi Unmarshal İşlemi
- v1 davranışı: Go dizisine (
[N]T) unmarshal ederken JSON dizisinin uzunluğu Go dizisinin uzunluğuyla farklı olsa da hata oluşturmaz. Uzunluk kısaysa kalan alanlar sıfır değerle doldurulur, uzunsa fazla kısım atılır. - v2 davranışı: JSON dizisinin uzunluğu Go dizisinin uzunluğuyla tam olarak eşleşmelidir. Aksi takdirde hata oluşur.
- Değişikliğin nedeni: Go'da sabit boyutlu dizilerin uzunluğu çoğu zaman önemli bir anlam taşır. v1'in davranışı, verinin sessizce kaybolmasına yol açabilir. v2, daha katı kurallarla doğruluğu artırmıştır.
- İlgili seçenekler: v1'in davranışını taklit etmek için
json.UnmarshalArrayFromAnyLength(true)seçeneği kullanılabilir.
6. time.Duration İşleme
- v1 davranışı:
time.Duration, dahili olarakint64gibi ele alınır ve nanosaniye cinsinden bir JSON sayısı olarak kodlanır. - v2 davranışı:
time.Duration.String()metodu kullanılarak"1h2m3s"gibi biçimde bir JSON dizgisi olarak kodlanır. - Değişikliğin nedeni: Nanosaniye cinsinden sayılar okunabilirliği azaltır;
time.Duration'ın standart dizgi gösterimi daha kullanışlıdır. - İlgili seçenekler: v1'in davranışını kullanmak için
json:",format:nano"etiket seçeneği veyajson.FormatTimeWithLegacySemantics(true)seçeneği kullanılabilir.
7. Geçersiz UTF-8 İşleme
- v1 davranışı: Marshal/unmarshal sırasında bir dizgi içinde geçersiz UTF-8 baytları varsa bunları Unicode yedek karakteri (
\uFFFD) ile sessizce değiştirir. - v2 davranışı: Varsayılan olarak geçersiz UTF-8 ile karşılaşıldığında hata döndürür.
- Değişikliğin nedeni: Verinin sessizce bozulmasını önlemek ve daha katı JSON standardını (RFC 7493) izlemek amaçlanmıştır.
- İlgili seçenekler: v1'in davranışını taklit etmek için
jsontext.AllowInvalidUTF8(true)seçeneği kullanılabilir.
8. Yinelenen Nesne Üye Adlarının İşlenmesi
- v1 davranışı: Bir JSON nesnesinde aynı adlı üyelerin yinelenmesine izin verir. Son görülen değer önceki değerin üzerine yazılır.
- v2 davranışı: Varsayılan olarak yinelenen üye adı bulunduğunda hata döndürür.
- Değişikliğin nedeni: RFC 8259 standardı yinelenen adların davranışını tanımlamadığından, uygulamalar farklı davranabilir. Bu durum bir güvenlik açığının kaynağı olabilir. v2, bunu açıkça reddederek doğruluğu ve güvenliği artırmıştır.
- İlgili seçenekler: v1'in davranışını taklit etmek için
jsontext.AllowDuplicateNames(true)seçeneği kullanılabilir.
Uygulama ve Mimari Farklılıklar
- v1:
decode.goiçindekidecodeStatevescanner.goiçindeki elle yazılmış durum makinesine (state machine) büyük ölçüde bağımlıdır. Bu, ayrıştırma mantığı ile anlamsal analizin sıkı biçimde birleşik olduğu tekparça (monolitik) bir yapıdır. - v2: Mimari daha modüler hale getirilmiştir.
encoding/json/jsontext: Düşük seviyeli, yüksek performanslı bir JSON belirteçleyicisi (tokenizer) (Decoder) ve kodlayıcısı (Encoder) sunar. Bu paket yalnızca JSON'un sözdizimsel (syntax) yönlerine odaklanır.encoding/json/v2:jsontextüzerine inşa edilmiştir ve Go türleri ile JSON değerleri arasındaki anlamsal (semantic) dönüşümleri yönetir.- Bu ayrım sayesinde sözdizimsel ayrıştırma ile anlamsal analiz birbirinden ayrılmış, kodun açıklığı ve performansı artmıştır.
v2'nin Yeni API ve Özellikleri
v2, json.Options sistemi aracılığıyla çok esnek bir denetim imkânı sunar.
json.Options: Marshal/unmarshal davranışını değiştiren seçeneklerin kümesidir.json.JoinOptions(...): Birden fazla seçeneği tek bir seçenekte birleştirir.WithMarshalers/WithUnmarshalers:Marshaler/Unmarshalerarayüzlerini uygulamadan belirli bir tür için serileştirme/seri durumdan çıkarma mantığı eklemeye olanak tanıyan güçlü bir özelliktir. Bu, özellikle harici paketlerdeki türlerle çalışırken yararlıdır.- Yeni seçenekler:
RejectUnknownMembers,Deterministic(false),FormatNilSliceAsNullgibi v1'de mümkün olmayan çeşitli davranış denetimleri artık kullanılabilir.
Sonuç
encoding/json v2, v1'deki deneyimlerden yola çıkarak doğruluk, performans ve esnekliği önemli ölçüde geliştiren çağdaş bir uygulamadır. Varsayılan davranışlar daha katı hale gelmiş olsa da, gelişmiş Options sistemi sayesinde v1'in tüm davranışları tam olarak desteklenir; böylece mevcut kodla uyumluluk korunarak v2'nin avantajları kademeli olarak benimsenebilir.
- Yeni projeler için v2'nin varsayılan olarak kullanılması önerilir.
- Mevcut projeler,
jsonv1'i olduğu gibi kullanmaya devam edebilir veyajsonv2'ye geçiş yaparkenDefaultOptionsV1()aracılığıyla v2'nin katı davranışlarını kademeli olarak benimseyen bir strateji izleyebilir.