May 28, 2012
  1. 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;

  1. 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. ]

May 28, 2012

Evet, 2.59'da derleniyormuş 2.58'de derlenmiyor. Bu durumda yeni bir özellik kullanmış olmalıyız ki bu kodu anlamam için bir hafta süreye ihtiyacım var...:)

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

May 28, 2012

Alıntı (acehreli):

>
/* 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ı!");

Ali ben bu kısmı anlamadım! Buradan benim anladığım hataAtıldı_mı değişkenini false değeri ile ilklendiriyoruz ardından to!JSONValue(ulong.max); metodunu ulong.max değeri ile çağırıyoruz. Doğal olarak bu durumda to!JSONValue() metodu içindeki

enforce(değer <= long.max,
                   format("Veri kaybı: Bir %s değeri olan %s, long'a sığamaz!",
                          T.stringof, değer));

metodu yanlışlığı yakalıyor ve bir hata fırlatıyor. Bu hatayı catch bölümünde tekrar yakalıyor ve hataAtıldı_mı değişkeninin değerini true yapıyoruz. Bir alt satırda assert hataAtıldı_mı değişkenini işliyor ama değişken true değerinde olduğu için assert bir hata olmadığını kabul ederek program akışına müdahale etmiyor. Oysa hata oluşmuştu ?

Diğer taraftan burada 'scope' yapısını kullanmakta mümkün olabilir mi?

Alıntı:

>

Şimdi 64 bit ile de derledim ve aynı hatayı aldım. Şu dosyayı dener misiniz?

http://codepad.org/sDG4CJ7K/raw.d

Salih o dosya bende çalışıyor.
Sistem : dmd 2.059, Debian 6, 32 bit

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

May 28, 2012

İzninizle şu kısmı değiştirmeyi öneriyorum...
Alıntı:

>
> JSONValue JSONBelgesi()
> {
>     JSONValue json;
>     json.type = JSON_TYPE.OBJECT;
>     return json;
> }
> ```

>
Yerine, 'main()' içinde tek satır ekleyerek (8-10 satır çıkararak) şu şekilde kullansak?

JSONValue belge;
belge.type = JSON_TYPE.OBJECT;
belge.object["öğrenciler"] = to!JSONValue(öğrenciler);


Bu şimdilik basit ve küçük bir katkım olabilir. Çünkü henüz tüm kodu kavrayamadım. Kavramayı kolaylaştırmak içinde gereksiz bir işlevi kaldırarak işleri çok küçük bir miktar da olsa basitleştirdim. En azından tersine mühendislik ile kodu incelerken sondaki bir adım kolalaştırmış oldum...

Tüm öğrenicilere başarılar...:)

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
May 29, 2012

Yavaş yavaş anlamaya başlıyorum...

Önceki koddan alıştığımız enum içindeki D kodlarını ('opCast()' işlevini) saymazsak kilit nokta aşağısı:

@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;
}

@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;
}

Aslında her şey bir büyü (şablon) değil mi hocam? Yani derleme zamanında taşlar yerine oturuyor ve biz geliştiricilere yapıyı oluşturup JSON'u yazmaktan başka bir şey bırakmıyor!

Duygularımı kodlara dökmek için müthiş isminde bir değişken ekledim ve 'main()' içindeki karşılığına da int.max yazdım. Gerçekten de müthiş...:)

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

May 28, 2012

Bilmiyorum. 32-64 bit farkı sandım ama değil. Acaba tanımların sıralarını mı değiştirdin? Birden fazla modül mü kullandın? Yanlış olduğunu söylemiyorum tabii; yalnızca anlamaya çalışıyorum.

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

May 29, 2012

Merhaba,

Küçümsemek veya emeği küçültmek için söylemiyorum; aksine hoşuma gittiği için önemle belirtmeliyim ki 15 satırlık bir mixin işlevi olayı kopartıyor. Ali hocam bana kızacak ama verdiği emeğinin bir kısmını tek işleve küçülttüm. En azından bunu anlayabilmek ve tabloya bir anda bakabilmek için yaptım. Gerçekten çok leziz bir çözümmüş.

Teşekkürler Ali hocam...

/*
   jsonyaz.d (28.05.2012 - Ali Çehreli)
*/
import std.stdio, std.json;
import std.traits; // isSomString, isArray vb. ifadeler için
   :    :    :
void main()
{
   JSONValue belge;
   auto öğrenciler = [ Öğrenci("Ayşe", 12,
                              ["matematik" : 90,
                               "ingilizce" : 100 ]),
                       Öğrenci("Başak", 34,
                              ["matematik" : 95,
                               "ingilizce" : 99 ])
                     ];
   auto öğretmenler = [ Öğretmen("Mustafa Kemal", "Türkçe"),
                        Öğretmen("Ali Çehreli", "D Dili")
                      ];
   with(belge){
       type = JSON_TYPE.OBJECT;
       object["öğrenciler"] = to!JSONValue(öğrenciler);
       object["öğretmenler"] = to!JSONValue(öğretmenler);
   }
   writeln(toJSON(&belge));
}/* ÇIKTISI:
{
   "öğrenciler":[
   {
       "isim":"Ayşe",
       "numara":12,
       "notlar":
       {
           "ingilizce":100,
           "matematik":90
       }
   }, {
       "isim":"Başak",
       "numara":34,
       "notlar":
       {
           "ingilizce":99,
           "matematik":95
       }
   }
   ],
   "öğretmenler":[
   {
           "ismi":"Mustafa Kemal",
           "dersi":"Türkçe"
   }, {
           "ismi":"Ali Çehreli",
           "dersi":"D Dili"
   }
   ]
}*/

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

May 28, 2012

Bu tarafta bir sorun yok. (?)

dmd 2.059, 64 bit Linux (ama -m32 de sorunsuz).

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

May 29, 2012

Alıntı (Salih Dinçer):

>

Yerine, 'main()' içinde tek satır ekleyerek (8-10 satır çıkararak) şu şekilde kullansak?

>     JSONValue belge;
>     belge.type = JSON_TYPE.OBJECT;
>     belge.object["öğrenciler"] = to!JSONValue(öğrenciler);
> ```

>

Bence bir sakıncası yok. Ancak JSONBelgesi() metodunun JSON_TYPE.OBJECT tipini sarmalaması bana oldukça güzel görünüyor. Bu hem programı küçük parçalara bölme açısından hemde bu metodun çağrıldığı yerdeki temiz yazım ve okunabilirlik açısından bence değerli bir metot, senin kullandığın şekilde tabi ki olabilir.

Aslında bu metodu kodlayan Ali'ye sormak gerek böyle bir metot oluşturuken amacı neydi, neticede bu üç satır kodu bende büyük ihtimalle main metoduna iliştirip geçerdim.

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
May 29, 2012

Geliştirme aşamasında parçalara bölmenin faydaları olmalı. Peki kodumuz oturduysa, birim testlerinden başarıyla geçtiyse; Ali hocanın şu son yaptığı mix in olayını tek işlevde kullanmamamızın bir mahsuru var mı?

string opCastJSONValue(T)() {
   enum işlevBaşı = `
       JSONValue opCast(T : JSONValue)() const @property
       {
           JSONValue json;
           JSONValue[string] yapınınÜyesi;
       `;
   enum işlevSonu = `
           json.type = JSON_TYPE.OBJECT;
           json.object = yapınınÜyesi;
           return json;
       }`;
   enum size_t xFazlalık = 3; /* Parantezler ve nokta */
   enum size_t soldanKes = T.stringof.length + xFazlalık;

   string katmaİfadesi = işlevBaşı;

   foreach (i, üyeTürü; typeof(T.tupleof)) {
       enum değişken = T.tupleof[i].stringof[soldanKes..$];
       enum solTaraf = `yapınınÜyesi["` ~ değişken ~ `"]`;
       enum sağTaraf = `to!JSONValue(` ~ değişken ~ `);`;
       katmaİfadesi ~= solTaraf ~ "=" ~ sağTaraf;
   }
   return katmaİfadesi ~ işlevSonu;
}

struct Öğrenci
{
   string isim;
   ulong numara;
   uint[string] notlar;
   // Katma (mixin) ifadeleri, derleme anında buraya gelecek...
   mixin(opCastJSONValue!Öğrenci);
}

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]