JSON Biçimlendirme, Doğrulama ve Şema: Pratikte
Özet (TL;DR)
Geçen ay birlikte çalıştığım bir ekip, feature_flags.json dosyasına tek satırlık bir düzenleme gönderdi. JSON.parse kabul etti, CI yeşil geçti ve staging’e indiği anda her ödeme dalı eski kod yoluna düştü; çünkü flags.checkout_v2 alanı boolean true yerine dize "true" idi. “JSON’ı kontrol etmek” sessizce üç farklı etkinliğe ayrılmıştı ve ekip yalnızca ilk ikisini yapmıştı. Güzel yazdırma (pretty-printing), boşlukları yeniden düzenleyerek bir insanın veriyi okuyabilmesini sağlar; hiçbir şeyi doğrulamaz. Sözdizim doğrulama (syntax validation), bir dizenin RFC 8259’a göre ayrıştırılabilir JSON olduğunu onaylar — eşleşen köşeli parantezler, doğru tırnaklama, geçerli sayı hazır bilgileri — ve JSON.parse tam olarak bunu yapar. Yapısal doğrulama (structural validation), programınızın beklediği şekle ayrıştırılan değerin uyup uymadığını soran ayrı bir adımdır: zorunlu alanlar, doğru tipler, izin verilen enum değerleri, dize uzunlukları, sayısal aralıklar. JSON Schema tam da bu son adım için tasarlandı ve çoğu ekip yukarıdaki gibi bir üretim hatası onları eklemeye zorlayana kadar bunu atlar. Güvenilir bir boru hattı üçünü de doğru katmanda kullanır: okumak için biçimlendirin, hatalı biçimli girdiyi yakalamak için ayrıştırın ve güven sınırlarında — gelen API istekleri, yapılandırma dosyaları ve servisler arası mesajlar — Ajv tarzı bir şema kontrolü çalıştırın.
Arka plan ve kavramlar
JSON, RFC 8259 (ve eşdeğeri ECMA-404) ile tanımlanır. Dilbilgisi kasıtlı olarak küçüktür. Bir JSON belgesi şunlardan biridir: dize, sayı, true, false, null, dizi veya nesne. Dizeler çift tırnaklıdır ve \n, \t, \", \\ ve Unicode kod birimleri için \uXXXX gibi kısa bir kaçış listesini destekler. Sayılar isteğe bağlı işaret, kesirli kısım ve üs ile ondalık dilbilgisini izler; ancak sözdizim düzeyinde tamsayı ve kayan nokta arasında bir ayrım yoktur. Nesneler dize anahtarlı üyelerin sıralanmamış koleksiyonlarıdır, diziler sıralı listelerdir ve dizeler dışındaki boşluklar önemsizdir.
JSON’ın kasıtlı olarak içermedikleri de neredeyse aynı derecede önemlidir. Yorum yoktur, iz virgülü (trailing comma) yoktur, tek tırnaklı dize yoktur ve hex ya da ikili hazır bilgi yoktur. ASCII aralığı dışındaki Unicode karakterler ya kodlanmış akışta ham UTF-8 baytları olarak ya da \u kaçışları olarak görünmelidir. JSON5 ve HJSON gibi uzantılar bu kuralların bazılarını gevşetir, ama bunlar ayrı formatlardır ve katı JSON kabul eden ayrıştırıcılar onları reddeder.
Bir belge ayrıştırıldıktan sonra, sözdizim geçerliliği doğru veri olup olmadığı hakkında hiçbir şey söylemez. {"user": "x", "pass": "y"} gibi bir oturum açma yükü, uç noktanız {"username": "...", "password": "..."} beklese bile mükemmel derecede geçerli bir JSON’dur. Bu tür hataları yakalamak için bir şemaya ihtiyacınız var: neyin kabul edilebilir belge sayıldığının makine okunabilir açıklaması. JSON Schema (şu anda önerilen meta şema Draft 2020-12) bu boşluğu doldurur. Zorunlu alanları, tip kısıtlamalarını, enum ve const’ı, regex ile dize desenlerini, sayısal aralıkları, dizi items ve benzersizliğini, nesne properties ve additionalProperties’i, allOf, oneOf, anyOf ve $ref ile bileşimi destekler. Ajv 8.12, bir şemayı tek seferde JavaScript doğrulayıcı işlevine derler; bu, sıcak yollar için yeterince hızlıdır — Node 20.11 üzerinde enstrümante ettiğim bir hizmette, tipik bir istek gövdesinde derlenmiş Ajv kontrolü onlarca mikrosaniye aralığındaydı.
Üçü arasında güzel yazdırma en sıradan olanıdır. Yalnızca boşluk ekler — girinti ve satır sonları — anlamı değiştirmeden. Çoğu editör ve komut satırı aracı bunu yapar; tarayıcı geliştirici araçları ağ panelinde otomatik olarak yapar. İnsanlar için yararlı, makineler için önemsizdir.
Karşılaştırma ve veriler
| Boyut | Güzel yazdırma | Sözdizim doğrulama | JSON Schema doğrulama |
|---|---|---|---|
| Amaç | Veriyi okunur kılmak | Metnin JSON olarak ayrıştığını doğrulamak | Ayrıştırılan verinin beklenen şekle uyduğunu doğrulamak |
| Yakalar | Hiçbir şey — yalnızca boşluk | Eşleşmeyen köşeli parantezler, kötü tırnaklama, geçersiz kaçışlar, iz virgülleri | Eksik alanlar, yanlış tipler, aralık dışı değerler, bilinmeyen anahtarlar |
| Yakalamaz | Yapısal sorunlar, anlamsal sorunlar | Yapısal sorunlar (beklenen şekil), iş kuralları | Şemanın ötesindeki anlamsal kurallar, özel anahtar sözcük olmadan alanlar arası değişmezler |
| Tipik araçlar | JSON.stringify(obj, null, 2), jq, IDE biçimlendirici | JSON.parse, jq -e, herhangi bir ayrıştırıcı | Ajv 8.x, python-jsonschema, OpenAPI doğrulayıcıları |
| Nerede çalıştırılır | Geliştirici araçları, günlükler | Her ayrıştırma sınırında (örtük) | API istek/yanıt, yapılandırma yüklemesi, mesaj sınırları |
Üç sütun alternatifler değil; ardışık katmanlardır. 12 MB’lık tek satırlık bir package-lock.json’u ham bir editörde açmak, fark aracınızın karşılaştırmayı reddettiği bir dizeyle biten yoldur ve girintili biçime güzel yazdırmak hiçbir şeyi doğrulamaz — yalnızca şekli okunur kılar. Bu ayrım önemlidir çünkü sonraki iki katman — sözdizim ve şema — çoğu zaman “güzel yazdırdım ve hiçbir şey patlamadı” diye yanlış anlaşılır. Okumak için güzel yazdırın, hatalı biçimli metni yakalamak için ayrıştırın ve yanlış şekli yakalamak için bir şemayla doğrulayın. Yalnızca ilk ikisini göndermek yaygın bir boşluktur.
Gerçek senaryolar
Senaryo 1 — Bir API yanıtını hata ayıklama. Üçüncü taraf bir ödeme ağ geçidi 600 satırlık bir JSON gövdesi döndürüyor ve tarayıcı onu tek satır olarak gösteriyor. Güzel yazdırmak — tarayıcı geliştirici araçlarıyla, yerel bir biçimlendiriciyle veya curl ... | jq . ile — onu bir insanın göz gezdirebileceği bir şeye dönüştürür. Burada doğrulama olmaz; amaç, yanlış görünen alanı ararken okunabilirliktir ve önemli disiplin bu adımı “doğrulandı” olarak adlandırmamaktır.
Senaryo 2 — Bir yapılandırma dosyası yüklemek. Bir hizmet başlangıçta config.json’u okur. Katı bir JSON ayrıştırıcı, serseri bir iz virgülü gibi sözdizim hatalarını yakalar ve başlamayı reddeder; bu doğru davranıştır. Ancak açılış olayının gösterdiği gibi, retries: 3 yerine retries: "three" içeren geçerli bir dosya sorunsuz ayrıştırılır ve yalnızca kod bir dizeyi bir sayıyla karşılaştırmaya çalıştığında başarısız olur. Benim için işe yarayan desen, Ajv.compile(schema)(config)’ı giriş noktasının ilk satırlarına koymak ve başarısızlıkta process.exit(1) çağırmaktır — birinin dosyayı elle düzenlediği sonraki iki seferde bedelini çıkaran beş dakikalık bir değişiklik.
Senaryo 3 — Bir OpenAPI sözleşmesini doğrulamak. Bir ekip, uç noktaları, istek gövdelerini ve components.schemas altındaki yanıt şekillerini açıklayan bir OpenAPI 3.1 belgesi gönderir. Sözleşme testleri, şartnameden örnek yükleri alır ve bir JSON Schema doğrulayıcı kullanarak şemalara karşı doğrular. Bir sunucu uygulaması kayarsa — diyelim ki şartnamenin bir dize vaat ettiği yerde bir tam sayı döndürmeye başlarsa — sözleşme testi, bir istemci üretimde bozulmadan önce uyumsuzluğu işaretler. Bu, yalnız bir yapılandırma dosyası için kullanacağınız aynı JSON Schema motorudur; fark kapsamın ölçeğidir.
Yaygın yanlış anlamalar
“JSON.parse JSON’ı doğrulamak için yeterlidir.” Sözdizimi doğrular, şekli değil. Bir ayrıştırıcı, kodunuzun ihtiyaç duyduğu alanların yarısı eksik olan bir nesneyi memnuniyetle döndürür. JSON.parse’ı hatalı biçimli metne karşı bir kapı olarak kabul edin ve veri bir güven sınırını geçtiğinde üstüne bir şema kontrolü katmanlayın.
“JSON Schema yalnızca sunucu tarafıdır.” Bir istek göndermeden önce tarayıcıda doğrulamak, kullanıcılara anında geri bildirim verir ve sunucu yükünü azaltır. Birçok form kitaplığı ve Ajv’in kendisi tarayıcıda rahatça çalışır. Sunucu tarafı doğrulama hâlâ çalışmalıdır — istemciye asla güvenmeyin — ancak istemci tarafı kontroller güvenlik modelini zayıflatmadan UX’i iyileştirir.
“JSON5 yorumları olan JSON’dır.” JSON5; yorumlar, tırnaksız anahtarlar, iz virgülleri, hex sayılar ve daha fazlasını ekler. Bu, onu bir insan tarafından düzenlenen yapılandırma formatı olarak daha dostça yapar — en iyi bilinen tüketici, aslında JSONC (farklı bir standart dışı üst küme) kullanan tsconfig.json’dur — ancak RFC 8259’u katı biçimde izleyen hiçbir şey onu kabul etmez. JSON5/JSONC’yi tüketicinin desteklediğini belgelediği yerde kullanın; ağa veya ayrıştırıcısını kontrol etmediğiniz herhangi bir araca yazarken katı JSON yayın.
“YAML girintili JSON’dır.” Her geçerli JSON belgesi geçerli YAML’dir, ancak YAML özellikler ekler — çapalar ve takma adlar, etiketli tipler, tek dosyada birden çok belge, blok ve katlanmış skaler, girintiye duyarlı ayrıştırma — ve bunlar JSON’da olmayan hatalar getirir. Klasik olanı sözde “Norveç sorunu”dur: YAML 1.1, tırnaklamadığınız sürece NO’yu boolean false olarak yorumlar. JSON’dan YAML’a “okunur kılmak için” geçmek, bir sorun sınıfını başka biriyle takas eder.
Kontrol listesi
- Yalnızca veriyi okumanız mı gerekiyor? Güzel yazdırın. “Doğrulandı” iddiasında bulunmayın.
- Bir güven sınırını mı geçiyor (HTTP isteği, mesaj kuyruğu, yapılandırma dosyası)? Ayrıştırın ve ardından yalnızca ayrıştırma değil, bir JSON Schema kontrolü çalıştırın.
- Bilinmeyen alanları önemsiyor musunuz? Şemada
additionalProperties: falseayarlayın ve fazlalıkları reddedip reddetmeyeceğinize ya da kesip atacağınıza karar verin. - Hatalar eyleme dönük mü? Kullanıcıların tüm sorunları bir kerede görmesi için doğrulayıcıyı (örneğin Ajv’i
allErrors: trueile) her ihlali döndürecek şekilde yapılandırın. - Şema, veriyi kullanan kodun yanında mı yaşıyor? Bir şartname ile uygulama arasındaki kaymayı önlemek, şema her ikisi için de bir gerçek kaynağı olduğunda daha kolaydır.
- Format JSON için yeterince kararlı mı? İnsanların düzenlemesi gerekiyorsa ve ayrıştırıcıyı siz kontrol ediyorsanız, JSON5/JSONC veya TOML daha bağışlayıcı olabilir. Makineler bunu değiş tokuş ediyorsa katı JSON’da kalın.
İlgili araç
Patrache Studio JSON biçimlendiricisi tarayıcıda çalışır, böylece yapıştırdığınız yük makineden hiç ayrılmaz — kişisel veri içeren bir üretim yanıtını incelerken yararlı. Yükler nadiren yalnız yaşar: biçimlendirdiğiniz JSON gömülü bir ikili içeriyorsa, Base64 ve URL Kodlaması: Amaç, Tuzaklar, Doğru Kullanım içindeki kurallar blobun neden genişlediğini ve ne zaman farklı bir taşıma uygun olduğunu açıklar. Yük kimlikler içeriyorsa, UUID v1, v4 ve v7 Karşılaştırması: Bir Veritabanı Birincil Anahtarı Seçmek sunucuda ürettiğiniz kesin sürümün alt sistemlerin o JSON’u nasıl indekslediğini, sıraladığını ve önbelleğe aldığını neden etkilediğini anlatır.
Kaynaklar
- IETF RFC 8259, “The JavaScript Object Notation (JSON) Data Interchange Format” — https://datatracker.ietf.org/doc/html/rfc8259
- JSON Schema Şartnamesi (Draft 2020-12) — https://json-schema.org/specification
- Ajv — Another JSON Schema Validator — https://ajv.js.org/
- MDN, “Working with JSON” — https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON