Taslak Çalışmalarım

English (Ä°ngilizce)

Sunday, August 14, 2005

JibX ile XML-Java Mapping

JibX ile XML-Java nesneleri arasında mapping yapılmasına yönelik bazı temel komutları, referans dokümanı şeklinde hazırladım. Zaman içinde yeni eklemeler yapacağım:

mapping

Bir java nesnesinin eşleştirme tanımlamasını başlatır:

<mapping name="customer" class="com.yunus.xml.jibx_1.Customer">

 

direction

Eğer nesne-XML bağlama işini tek yönlü yapacaksan, bunu belirterek, bazı tanımlamalardan tasarruf edebilirsin.

direction="input" veya direction="output"

 

structure

Structure, bir java nesnesine eşleştirilir.

<binding>

<mapping name="customer" class="com.yunus.xml.jibx_1.Customer">

<structure name="person" field="person">

<value name="cust-num" field="customerNumber" />

<value name="first-name" field="firstName" />

<value name="last-name" field="lastName" />

</structure>

<value name="street" field="street" />

structure ve mapping birbirine benzer. İkisi de bir nesnenin nasıl XML´e eşleştirildiğini tanımlar. Ancak structure, bir java nesnesinin içindeki bir nesneyi tanımlar. Ayrıca structure genel kural koymaz. Yani aynı sınıfın nesneleri bir yerde bir şekilde eşleştirilip, diğer yerde farklı eşleştirilebilir.

XML structure

XML içindeki structure´lar Javadaki kompoze edilen bir nesneye karşılık gelmek zorunluluğu yok. Bunun için field kısmı boş tutulur. Yani field belirtilmez.

<mapping name="customer" class="example6.Customer">

<structure name="person">

<value name="cust-num" field="customerNumber"/>

<value name="first-name" field="firstName"/>

<value name="last-name" field="lastName"/>

</structure>

<customer>

<person>

<cust-num>123456789</cust-num>

<first-name>John</first-name>

<last-name>Smith</last-name>

</person>

public class Customer {

public int customerNumber;

public String firstName;

public String lastName;

 

Java structure

Javadaki bir nesneye karşılık XMLde herhangi bir structure bulunması zorunlu değil. Bunun için structure elementi, name atributu belirtilmeden tanımlanır:

<structure field="address">

<value name="street" field="street"/>

<value name="city" field="city"/>

public class Customer {

public Address address;

<customer>

<street>12345 Happy Lane</street>

<city>Plunk</city>

<state>WA</state>

 

--- Opsiyonel alanlar ---

Bir Java nesnesini herhangi bir XML alanına bağlamazsan, bunu XML dosyasında üretmez. Tersine, bir XML elementini bir structure ile tanımlar, ancak herhangi bir fielda bağlamazsan, bu veriyi Java nesnesine taşımaz.

<mapping name="customer" class="example8.Customer">

<value name="street" field="street"/>

<value name="city" field="city"/>

<value name="state" field="state"/>

<value name="zip" field="zip"/>

<structure name="instructions"/>

<customer>

<street>12345 Happy Lane</street>

<city>Plunk</city>

<state>WA</state>

<zip>98059</zip>

<instructions>

<!-- content skipped when unmarshalled -->

<p>Leave package behind bushes to <b>left</b>

of door</p>

<!-- always marshalled as empty -->

</instructions>

<phone>888.555.1234</phone>

</customer>

public class Customer {

public Person person;

public String street;

public String city;

public String state;

public Integer zip;

public String phone;

}

Person nesnesi, herhangi bir XML elemanına bağlanmamış. instructions elemanıysa, herhangi bir nesne alanına bağlanmamış.

--- name ---

name atributu, XML taginin ismini belirtir.

Eğer name belirtilmezse, bir üst elementin belirttiği şartlar geçerli olur:

<structure name="person" field="person"

value-style="attribute">

<value name="cust-num" get-method="getNumber"

set-method="setNumber"/>

<value name="first-name" field="firstName" />

<value field="lastName" style="text"/>

</structure>

<person cust-num="2993" first-name="Ahmet">Özgül</person>     

 

--- field ---

field atributu java nesnesinin member değişkenidir.

--- style ---

style ile XML içeriğinin text olarak mı, atribut olarak mı yazılacağı belirtilir.

<value field="lastName" style="text"/>

 

--- value ---

Primitif java verileri, value ile XML elemanlarına bağlanır.

<value field="lastName" style="text"/>

 

--- value-style ---

value´lar, XML elemanlarına (atribute veya element) karşılık gelir. value-style, XML elemanlarının tipini (atribute mu, element mi) olduğunu belirtir:

<structure name="person" field="person"

value-style="attribute">

<value name="cust-num" get-method="getNumber"

set-method="setNumber"/>

<value name="first-name" field="firstName" />

<value field="lastName" style="text"/>

</structure>

<person cust-num="2993" first-name="Ahmet">Özgül</person>

 

--- usage ---

Java nesnesinden XML´e dönüştürme sırasında herhangi bir elemanın zorunlu mu, opsiyonel mi olduğunu belirtir.

<value name="zip" field="zip" usage="optional" />

Java -> XML

<customer>

<person cust-num="2993" first-name="Ahmet">Özgül</person>

<street>Mertebe</street>

<city>Emek</city>

<state>Ankara</state>

<phone>+90(535)7120767</phone>

</customer>

XML -> Java dönüşümünde, eğer zip yoksa, zip değişkenine null atanacaktır.

--- get-method set-method ---

Bir XML elemanına karşılık gelen nesne içeriğinin getter ve setterını belirtir:

<value name="cust-num" get-method="getNumber"

set-method="setNumber"/>

 

--- collection ---

Bir java nesnesindeki collection nesnesini eşleştirir:

<mapping name="timetable" class="com.yunus.xml.jibx_4.TimeTable">

<collection field="carriers" item-type="com.yunus.xml.jibx_4.Carrier"/>

public class TimeTable {

private ArrayList carriers;

Collection içeriği de özel nesnelerse, bunların eşleştirmesinin de tanımlanması gerekir:

<mapping name="carrier" class="com.yunus.xml.jibx_4.Carrier">

item-type zorunlu değil. Ancak item-type belirtilmezse, collection içinde her türlü nesne bulunabilir. Bu durumda XML->Java dönüşümü istenildiği gibi olmaz.

--- wrapper ---

Collection içindeki nesnelerin, bir wrapper içinde gösterilmesi için:

<timetable>

<carriers>

<carrier code="NL" rating="4">

Her bir collection tanımına, bir name belirtmelisin:

<collection name="carriers" field="carriers"/>

 

--- ordered ---

XML dokümanının binding.xml dosyasında belirtilen sırada olması varsayımını kaldırır. Ancak bu xml-java dönüştürme işini yavaşlatır.

<mapping name="customer" class="example3.Customer" ordered="false">

 

--- ident referans ---

Java nesneleri arasındaki referansları XML´de de koruyabilmek için, XML´in ID ve IDREF özellikleri kullanılır.

<mapping name="airport" class="example5.Airport">

<value style="attribute" name="code" field="code" ident="def"/>

<mapping name="route" value-style="attribute" class="example5.Route">

<value name="from" field="from" ident="ref"/>

<value name="to" field="to" ident="ref"/>

XML:

<route from="BOS" to="SEA">

 

--- namespace ---

namespace hangi element ve atributlara uygulanacağı, default atributuyla belirtilir:

<mapping name="customer" class="example9.Customer">

<namespace uri="http://www.sosnoski.com/ns1";

default="elements"/>

<customer phone="888.555.1234"

xmlns="http://www.sosnoski.com/ns1">;

Eğer default="all" belirtilirse, bütün alt element ve atributlarda namespace uygulanır:

<mapping name="person" class="example9.Person">

<namespace prefix="ns2"

uri="http://www.sosnoski.com/ns2"; default="all"/>

<ns2:person ns2:cust-num="123456789"

xmlns:ns2="http://www.sosnoski.com/ns2">;

<ns2:first-name>John</ns2:first-name>

<ns2:last-name>Smith</ns2:last-name>

</ns2:person>

Eğer üst bir elemanda belirtilmiş namespace´i iptal etmek istiyorsan, ns atributunu kullanmalısın.

--- abstract ve extends ---

abstract olarak belirttiğin sınıfları extend eden sınıfları belirtebilirsin. Böylece, soyut sınıfa ait xml verisi, her iki extend eden sınıfta da kullanılabilir:

<binding>

<mapping name="customer" class="example10.Customer">

<structure field="identity"/>

</mapping>

<mapping class="example10.Identity" abstract="true">

<value name="cust-num" field="customerNumber"/>

</mapping>

<mapping name="person" class="example10.Person" extends="example10.Identity">

<structure map-as="example10.Identity"/>

<value name="first-name" field="firstName"/>

</mapping>

<mapping name="company" class="example10.Company" extends="example10.Identity">

<value name="tax-id" field="taxId"/>

<structure map-as="example10.Identity"/>

</mapping>

</binding>

<customer>

<person>

<cust-num>123456789</cust-num>

<first-name>John</first-name>

</person>

</customer>

<customer>

<company>

<tax-id>91-234851</tax-id>

<cust-num>311233459</cust-num>

</company>

</customer>

 

--- using ve label ---

Bir structure, mapping veya collection ile tanımlanmış eşleştirmeyi tekrar tekrar farklı yerlerde kullanabilirsin.

<structure name="ship-address" field="shipAddress" label="base-address">

<value name="street" field="street"/>

<value name="city" field="city"/>

<value name="state" field="state"/>

<value name="zip" field="zip"/>

</structure>

<structure name="bill-address" field="billAddress" using="base-address"

usage="optional"/>

<ship-address>

<street>12345 Happy Lane</street>

<city>Plunk</city>

<state>WA</state>

<zip>98059</zip>

</ship-address>

<bill-address>

<street>54321 Happy Lane</street>

<city>Plunk</city>

<state>WA</state>

<zip>98059</zip>

</bill-address>

Ancak bu kullanım şekli 2. versiyonda değiştirilecek. abstract yaklaşımını şimdi daha ziyade kullanmakta fayda var.

--- pre-get pre-set post-set factory ---

factory metodu bir nesne serisizleştirilmeden (unmarshall) önce çağrılır. pre-get metodu, nesne serileştirmeden önce çağrılır.

<mapping name="order" class="example12.Order"

factory="example12.Order.orderFactory" pre-get="preget">

public static Order orderFactory() {

System.out.println("Order.orderFactory called");

return new Order();

}

public void preget() {

System.out.println("Order.preget called");

total = items.size() * 1.5;

}

pre-set metodu nesne serileştirilmeden önce çağrılır.

post-set metodu nesne serileştirilmenin ardında çağrılır. Burada nesneler arasında referanslar kurmak uygun olur.

<mapping name="customer" class="example12.Customer">

<structure name="person" field="person" pre-set="preset">

<mapping name="item" class="example12.Item" post-set="postset">

public void postset(IUnmarshallingContext ctx) {

order = (Order)ctx.getStackObject(1);

System.out.println("Item.postset called");

}

public void preset(Object obj) {

customer = (Customer)obj;

System.out.println("Person.preset called");

}

Object tipinden parametre alan pre-set metotları, XML dosyasındaki kapsayan nesnenin referansını parametre olarak gönderir.

post-set metotları, Context nesnesini parametre olarak alır. Bununla tüm binding bünyesindeki nesnelere erişmek mümkündür.

--- serializers ---

İki tip özel serializer belirtmek mümkün. 1. format elementiyle, belirli tipteki tüm değerler için özel serializer yazılabilir. 2. belirli bir alan ve eleman için özel olarak serializer belirtilebilir:

<binding>

<format type="int[]" serializer="example13.Conversion.serializeIntArray"

deserializer="example13.Conversion.deserializeIntArray"/>

...

<value name="total" field="total"

serializer="example13.Conversion.serializeDollarsCents"

deserializer="example13.Conversion.deserializeDollarsCents"/>

<value name="orders" field="orders"/>

public class Conversion

{

private Conversion () {}



public static String serializeDollarsCents(int cents) {

serializerlar, statik metotlar olmak durumundadır.


--- marshaller ---

Özel marshallerlar yazmak da mümkün. Bunlar belirli bir sınıfı nasıl serileştireceklerini bilir:

<mapping class="java.util.HashMap" name="map"

marshaller="example14.HashMapper" unmarshaller="example14.HashMapper"/>

public class HashMapper implements IMarshaller, IUnmarshaller, IAliasable

<map size="4">

<entry key="38193">

<customer state="WA" zip="98059">

<name first-name="John" last-name="Smith"/>

<street>12345 Happy Lane</street>

<city>Plunk</city>

</customer>

</entry>

<entry key="39122">

Friday, August 05, 2005

Primitif Veriler veya İçiçe Katmanlar

Kaliteli yazılım geliştirmeyle ilgili pekçok kural var. Bunlardan bir tanesi, diğer pek çok kuralı kaynağı olan bir kural: Encapsulation veya Abstraction. Yani Türkçesiyle, soyutlama veya kapsülleme (biraz tuhaf bir çeviri :). Biz soyutlama diyelim...

Soyutlama, primitif veriyle (yani integer, character, String gibi veriler) doğrudan doğruya iş yapmanın doğru olmadığını söylüyor. Çünkü primitif veriyi doğrudan doğruya istemci (client) tarafında manipüle etmek, servisin (service) kullanılmasını zorlaştırıyor. Her istemci, servis sunanın iç yapısına bağımlı oluyor.

Not: Servis kelimesini, herhangi bir hizmet sunan nesne veya fonksiyon anlamında, istemci kelimesini, herhangi bir fonksiyonu kullanan nesne anlamında kullanıyorum. Mesela,


class A {
void f() {
B b = new B();
b.g();
}
}


Burada A istemci tarafıdır. B servis sunan taraf. g() fonksiyonu da, sunulan servistir.

Yukarıdaki örnekte, A B´nin iç yapısından hemen hemen tamamıyla soyutlanmış (B nesnesini doğrudan A´nın kendisi oluşturmasından dolayı, tam soyutlanma sayılmaz.)

A, B´nin g() fonksiyonu içinde ne yaptığına dair hiçbir varsayımda bulunmuyor ve herhangi bir primitif veriyi, B´nin doğru çalışması için işlemiyor.

Buraya kadar her şey güzel.

Soyutlama ilkesinin bir başka sonucu daha var: Farklı işlerle uğraşan (sorumluluklara sahip) nesneler, farklı katmanlar (layers) içinde olmalı. Katmanlar arasındaki nesneler birbiriyle az iletişim kurmalı. Bir katmanda yürütülen fonksiyonalitenin bir kısmı veya benzeri başka bir katmanda yürütülmemeli.

Bu ilke, MVC (Model-View-Controller), DAO (Data Access Object) gibi pek çok dizayn kalıbının (design pattern) da temelini oluşturuyor. MVC´ye göre, görünüm katmanı (view) model (yani iş nesneleri) katmanına ait herhangi bir bilgiye sahip olmamalı.

Buraya kadar her şey güzel. Ancak şu problemimiz var: Görünüm nesneleri, model nesnelerine erişemedikleri halde nasıl bunların içindeki veriyi kullanıcıya gösterecek? Bu soruna, data binding (yani veri bağlama) sorunu deniyor.

Masaüstü uygulamalarında bunun güzel bir çözümü, gözlemci (observer) kalıbını kullanmak. Javada, JGoodies Binding çerçevesi (framework) bu işi çok kolaylaştırıyor. Ancak web uygulamalarında, bu çözüm işlemiyor. Farklı web çerçevelerinin farklı çözümleri var: Bazısı, Data Transfer Objectleri (DTO) kullanıyor, bazısı doğrudan model nesnelerinin propertylerini (yani verilerini) görünüm katmanına bağlayan ara bir katman kullanıyor...

DTO´lar sadece primitif veri içeren, model nesnelerinin birer kopyası. Ancak herhangi bir fonksiyonaliteleri yok. DRY (don´t repeat yourself) yani bir şeyin bir başka yerde yinelenmemesi ilkesini ihlal ediyor. Ek bir ara dönüştürücü katmana ihtiyaç duyuyor.

Doğrudan model nesnelerine erişmek, ilk bakışta güzel görünüyor. Ancak bu durumda da, katmanlaştırma ilkesi ihlal ediliyor. Çünkü görünüm nesneleri, model nesnelerine bağımlı kılınıyor. Bunun sebep olduğu en önemli sorunlardan biri, nesnelerin bayatlaması (stale). Yani görünüm katmanının eriştiği bir model nesnesi, veritabanındaki güncel halinden farklı veri tutabiliyor. Bu da pekçok bug oluşturuyor.

Bu sorunu çözmek için, nesnelere doğrudan referans yerine, belki onların veritabanından yeniden çekilebilmesini sağlayacak veritabanındaki primary keylerinin saklanması kullanılabilir. Ancak bu durumda da, soyutlama ilkesinin, primitif verilere erişimi sınırlama kuralı ihlal ediliyor.

Bu sorun üzerinde biraz daha yazmayı planlıyorum. Yorumlarınızı beklerim...