- bool konusu ilginçmiş çünkü JSON'a göre true ve false bir türün iki değeri değiller. true ve false başlı başlarına JSON türleri. O yüzden kendi bool değişkenimizin değerine göre yalnızca .type'ını belirlemek yeterli:
} else static if (is (T : bool)) {
json.type = değer ? JSON_TYPE.TRUE : JSON_TYPE.FALSE;
- null konusu da öyle: Bize bir gösterge verildiğinde eğer o göstergenin değeri null ise onu da başlı başına bir JSON türü olarak ifade ediyoruz. Ancak, null olmayan göstergelerle ne yapacağımızdan emin değilim. Zaten JSON'da bu konuda bir şey söyleyemez.
Düşünelim: Elimizde belleğin farklı noktalarında bulunan düğümlerden oluşan bir bağlı liste olsun. Düğümlerin 'sonraki' üyeleri göstergedir. O değeri olduğu gibi JSON dosyasına yazmak hiç işe yaramaz çünkü o dosyayı okuduğumuzda o adreslerde düğüm olmayacaktır.
O yüzden NULL JSON türü için ancak şunu yapabiliyorum:
} else static if (__traits(compiles, *değer)) {
if (!değer) {
json.type = JSON_TYPE.NULL;
} else {
/* Göstergeleri nasıl JSONValue olarak ifade edeceğimizi
* bilmiyoruz. */
assert(false);
}
T'nin bir gösterge olduğunu da *değer ifadesinin derlenebilmesinden anlıyorum.
Alıntı (Salih Dinçer):
>Sanırım bunu gösterge (pointer) ile yapabiliriz.
Ne yazık ki yeterli olmaz çünkü JSON bir metin düzeni. Nesnenin baytları üzerinde değil, üyeleri üzerinde gezinmek istiyoruz. Bununla ilgili olarak yukarıda bahsettiğim İngilizce konuyu okudum ve orada gösterilen çözümü uyarladım.
Amacımız, daha önceki Öğrenci yapısının opCast()'inde yaptığımız kadar uğraşmak zorunda kalmamak. Zor ve güvensiz bir iş: Yapıya bir eleman eklendiğinden opCast()'ını düzeltmeyi unutabiliriz, vs. Bu işi şu kadar kolay yapabilmeliyiz:
struct Öğrenci
{
string isim;
ulong numara;
uint[string] notlar; /* ders ismi : not */
bool boolDenemesi;
İçYapı içYapım;
float * f;
mixin(BütünÜyelerİçin_opCastJSONValue!Öğrenci);
}
O sondaki mixin() ifadesi bizim elle yazacağımız opCast() işlevini otomatik olarak halleder. Tanımı şöyle:
@property string BütünÜyelerİçin_opCastJSONValue(T)()
{
enum işlevBaşı = `
JSONValue opCast(T : JSONValue)() const @property
{
JSONValue json;
JSONValue[string] üyeler;
`;
enum eklemeİfadeleri = eklemeİfadeleri!(T, "üyeler");
enum işlevSonu = `
json.type = JSON_TYPE.OBJECT;
json.object = üyeler;
return json;
}
`;
return işlevBaşı ~ eklemeİfadeleri ~ işlevSonu;
}
Dikkat ederseniz daha önceki Öğrenci.opCast()'in tanımını oluşturuyor. Aradaki eklemeİfadeleri() ise başka bir şablon tarafından halledilecek. Burada D'nin derleme zamanındaki yardımlarından birisini kullanacağız. Şöyle bir döngü ile türün bütün üyelerini gezinebiliyoruz:
@property string eklemeİfadeleri(T, string tabloİsmi)()
{
enum size_t fazlalık = 3; /* Parantezler ve nokta */
enum size_t kırpılacaklar = T.stringof.length + fazlalık;
string eklemeİfadeleri;
foreach (i, üyeTürü; typeof(T.tupleof)) {
enum değişkenİsmi = T.tupleof[i].stringof[kırpılacaklar..$];
eklemeİfadeleri ~= eklemeİfadesi!(tabloİsmi, değişkenİsmi);
}
return eklemeİfadeleri;
}
Oradaki T.tupleof, türün bütün üyelerinden oluşan bir dizgi topluluğu döndürüyor. Orada neler döndüğünü anlamak istiyorsanız işe 'üyeTürü'nü çıktıya yazdırmakla başlayın. 'kırpılacaklar'a neden gerek olduğunu göreceksiniz...
O işlevin yararlandığı eklemeİfadesi() şablonu da aşağıdaki gibi. Ne yaptığı anlaşılsın diye unittest bloğunu da gösteriyorum:
@property string eklemeİfadesi(string tabloİsmi,
string değişkenİsmi)()
{
enum solTaraf = tabloİsmi ~ `["` ~ değişkenİsmi ~ `"]`;
enum sağTaraf = `to!JSONValue(` ~ değişkenİsmi ~ `);`;
return solTaraf ~ "=" ~ sağTaraf;
}
unittest
{
assert(eklemeİfadesi!("üyeler", "foo")
== `üyeler["foo"]=to!JSONValue(foo);`);
}
Sonuçta elimdeki bütün kod aşağıdaki gibi oluyor. Dikkat ederseniz Öğrenci yapısına bir de İçYapı ekledim. İçYapı'nın kendi opCast()'i tanımlı olduğu sürece herşey otomatik ve özyinelemeli olarak halloluyor:
import std.stdio;
import std.json;
import std.traits;
import std.exception;
import std.string;
import std.conv;
/*
* Verilen değeri JSONValue'ya dönüştürür.
*/
JSONValue to(Hedef : JSONValue, T)(T değer)
{
JSONValue json;
static if (isSomeString!T) {
json.type = JSON_TYPE.STRING;
json.str = std.conv.to!string(değer);
} else static if (__traits(compiles, *değer)) {
if (!değer) {
json.type = JSON_TYPE.NULL;
} else {
/* Göstergeleri nasıl JSONValue olarak ifade edeceğimizi
* bilmiyoruz. */
assert(false);
}
} else static if (is (T : bool)) {
json.type = değer ? JSON_TYPE.TRUE : JSON_TYPE.FALSE;
} else static if (is (T : long)) {
static if (is (T == ulong)) {
/* std.json long türünü kullandığı için ulong'un bazı değerlerini
* tutamaz. Güvenlik amacıyla bu durumu denetlemek için ulong için
* ayrı bir 'static if' koşulu yazıyoruz. */
enforce(değer <= long.max,
format("Veri kaybı: Bir %s değeri olan %s, long'a sığamaz!",
T.stringof, değer));
}
json.type = JSON_TYPE.INTEGER;
json.integer = değer;
} else static if (is (T : real)) {
json.type = JSON_TYPE.FLOAT;
json.floating = değer;
} else static if (isArray!T) {
json.type = JSON_TYPE.ARRAY;
foreach (eleman; değer) {
json.array ~= to!JSONValue(eleman);
}
} else static if (isAssociativeArray!T) {
json.type = JSON_TYPE.OBJECT;
JSONValue[string] elemanlar;
foreach (anahtar, eleman; değer) {
elemanlar[std.conv.to!string(anahtar)] = to!JSONValue(eleman);
}
json.object = elemanlar;
} else static if (__traits(compiles, cast(JSONValue)değer)) {
json = cast(JSONValue)(değer);
} else {
static assert(false,
"Bu tür JSONValue'ya dönüştürülemez: " ~ T.stringof);
}
return json;
}
unittest
{
import std.typetuple;
/* ulong'un değer aralığının üst yarısı long'a sığmaz. Onun için ayrıca
* deniyoruz. */
to!JSONValue(cast(ulong)0);
to!JSONValue(cast(ulong)long.max);
/* Güvenlik gerçekten işe yarıyor mu? */
bool hataAtıldı_mı = false;
try {
to!JSONValue(ulong.max);
} catch (Exception) {
hataAtıldı_mı = true;
}
assert(hataAtıldı_mı, "ulong.max için hata atılmadı!");
/* Her temel tür kullanılabilmeli. */
alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, /* ulong, */
bool,
int*,
float, double, real,
string, wstring, dstring, char[], wchar[], dchar[],
int[], string[double])
Türler;
foreach (Tür; Türler) {
to!JSONValue(Tür.init);
}
}
/*
* mixin() olarak kullanılabilecek bir işlev: Belirtilen JSONValue eşleme
* tablosuna belirtilen isimdeki değişkenin JSONValue karşılığını ekleyen
* ifadeyi dizgi olarak döndürür.
*/
@property string eklemeİfadesi(string tabloİsmi,
string değişkenİsmi)()
{
enum solTaraf = tabloİsmi ~ `["` ~ değişkenİsmi ~ `"]`;
enum sağTaraf = `to!JSONValue(` ~ değişkenİsmi ~ `);`;
return solTaraf ~ "=" ~ sağTaraf;
}
unittest
{
assert(eklemeİfadesi!("üyeler", "foo")
== `üyeler["foo"]=to!JSONValue(foo);`);
}
/*
* mixin() olarak kullanılabilecek bir işlev: Belirtilen türün bütün
* üyelerinin JSONValue karşılıklarını belirtilen tabloya ekleyen ifadeleri
* dizgi olarak döndürür.
*/
@property string eklemeİfadeleri(T, string tabloİsmi)()
{
enum size_t fazlalık = 3; /* Parantezler ve nokta */
enum size_t kırpılacaklar = T.stringof.length + fazlalık;
string eklemeİfadeleri;
foreach (i, üyeTürü; typeof(T.tupleof)) {
enum değişkenİsmi = T.tupleof[i].stringof[kırpılacaklar..$];
eklemeİfadeleri ~= eklemeİfadesi!(tabloİsmi, değişkenİsmi);
}
return eklemeİfadeleri;
}
unittest
{
struct BirYapı
{
int foo;
double bar;
}
assert(`üyeler["foo"]=to!JSONValue(foo);üyeler["bar"]=to!JSONValue(bar);`
== eklemeİfadeleri!(BirYapı, "üyeler"));
}
/*
* mixin() olarak kullanılabilecek bir işlev: Belirtilen türün bütün üyelerini
* teker teker JSONValue'lara dönüştüren ve nesneyi tek bir JSONValue olarak
* döndüren bir opCast() işlecinin tanımını dizgi olarak döndürür.
*/
@property string BütünÜyelerİçin_opCastJSONValue(T)()
{
enum işlevBaşı = `
JSONValue opCast(T : JSONValue)() const @property
{
JSONValue json;
JSONValue[string] üyeler;
`;
enum eklemeİfadeleri = eklemeİfadeleri!(T, "üyeler");
enum işlevSonu = `
json.type = JSON_TYPE.OBJECT;
json.object = üyeler;
return json;
}
`;
return işlevBaşı ~ eklemeİfadeleri ~ işlevSonu;
}
unittest
{
struct BirYapı
{
int foo;
double bar;
mixin(BütünÜyelerİçin_opCastJSONValue!BirYapı);
}
/* Derlenebilmesi yeterli. */
auto nesne = BirYapı();
auto json = cast(JSONValue)nesne;
}
struct İçYapı
{
int i = 42;
double d = 6.75;
mixin(BütünÜyelerİçin_opCastJSONValue!İçYapı);
}
struct Öğrenci
{
string isim;
ulong numara;
uint[string] notlar; /* ders ismi : not */
bool boolDenemesi;
İçYapı içYapım;
float * f;
mixin(BütünÜyelerİçin_opCastJSONValue!Öğrenci);
}
JSONValue JSONBelgesi()
{
JSONValue json;
json.type = JSON_TYPE.OBJECT;
return json;
}
void main()
{
auto öğrenciler = [ Öğrenci("Ayşe", 12,
[ "matematik" : 90, "ingilizce" : 100 ]),
Öğrenci("Başak", 34,
[ "matematik" : 95, "ingilizce" : 99 ]) ];
JSONValue belge = JSONBelgesi();
belge.object["öğrenciler"] = to!JSONValue(öğrenciler);
writeln(toJSON(&belge));
}
Çıktısı:
'{"öğrenciler":[{"boolDenemesi":false,"içYapım":{"d":6.75,"i":42},"isim":"Ayşe","numara":12,"notlar":{"ingilizce":100,"matematik":90},"f":null},{"boolDenemesi":false,"içYapım":{"d":6.75,"i":42},"isim":"Başak","numara":34,"notlar":{"ingilizce":99,"matematik":95},"f":null}]}'
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]