Annotation Kullanarak XStream
Önceki XStream ile ilgili yazıdaXStream kullanarak, XML dosyalarının manuel olarak nasıl oluşturulabileceğine bakmıştık. Örnek olarakta bir projeye ait veritabanı ayarlarının dosyadan alınmasını yapmıştık. Fakat projelerinizde düzenli olarak XStream kullanıyorsanız, belli bir yerden sonra manuel olarak XML dosyalarını oluşturma işinden sıkılıyorsunuz. Bunu Annotation kullaranak nasıl yapabileceğinizi düşünmeye başlıyorsunuz.
GeçtiÄŸimiz günlerde üzerinde çalıştığımız projede, XML dosyalarımızı okuma ve yaratma iÅŸlemini Java’nın kendi sınıflarından, XStream üzerine taşımaya karar verdik. Fakat beni sıkan ÅŸey manuel olarak XStream sınıflarını oluÅŸturmak oldu. Bundan dolayı bu iÅŸin annotation yapsının kullanılarak nasıl yapılacağını düşünmeye baÅŸladım. Sonradan bu durumun sadece beni sıkmış olmayacağının farkına vardım ve interneti araÅŸtırmaya koyuldum. Öğrendim ki zaten XStream içerisinde kolaylıkla kullanılabilen annotation yapıları mevcutmuÅŸ. İşte bu yazıda bu annotation yapılarının nasıl kullanılabileceÄŸimize bakacağız.
Giris
Öncelikle üzerinde çalışacağımız projeden bahsedelim. Basit bir konsol uygulması üzerinden anlatacağım. İsterseniz Android, Wicket ya da arzu ettiÄŸiniz baÅŸka framework’ler ve ortamlarda da rahatlıkla kullanabilirsiniz. Åžahsen geçen gün Android üzerinden baÄŸlandığım Rest web servisinden gelen XML cevabını nesneye dönüştürmek için XStream’i kullandım.
Her zaman ki gibi projemizi Maven kullanarak yöneteceÄŸim. Bu örnekte kullanacağımız maven archetype’ı klasik, maven-archetype-quickstart olacak. Bağımlılık olarak sadece aÅŸağıdaki XStream bağımlılığını girmeniz yeterli olacaktır.
<dependencies> <!-- XSTREAM DEPENDENCY --> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.2</version> </dependency> ... </dependencies>
Modeller ve Annotation
Basit olması açısından, bir kullanıcı sınıfı yaratalım. Basit olarak sadece id ve kullanıcı adı bilgisi taşısın. Daha sonra ise annotation kullanarak bu sınıfı yönetelim. Sınıfın XML dosyasına nasıl yazılacağını, ve XML Dosyasından nasıl okunacağını bir sonraki adımda anlatacağım.
package com.bahadirakin.xstream.model; import com.thoughtworks.xstream.annotations.XStreamAlias; @XStreamAlias("user") public class User { @XStreamAlias("id") private int id; @XStreamAlias("userName") private String userName; public User() { id = -1; userName = ""; } public User(int id, String userName) { super(); this.id = id; this.userName = userName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "UserName: " + userName + " with id: " + id; } }
XStream tarafından sağlanan ve en çok kullanacağınızı düşündüğüm annotation, XStreamAlias. Temel olarak nesnenin içeriğini annotation içerisinde verdiğiniz tag adının içerisine yerleştiriyor. Yani ileride user nesnesini XML olarak serileştirdiğimizde aşağıdaki XML çıktısını elde edeceğiz.
user.xml
<user> <id>1</id> <userName>bhdrkn</userName> </user>
Temel Annotation Sınıfları
[fancy_list style=”triangle_arrow” variation=”blue”]
- XStreamAlias: Bu annotation sınıfı, XStream sınıflarının belirtilmesinde kullanılıyor. Eğer sınıfınız başında XStreamAlias kullanmazsanız, XML oluştururken sorun yaşayabilirsiniz. Fakat alanlarınızda kullanmazsanız, alan adınız doğrudan tag olarak atanır. Bizim örneğimiz için eğer userName alanına XStreamAlias atamasaydık da sonuç değişmeyecekti.
- XStreamAsAttribute: Bu annotation, XStream sınıfınız içerisindeki bir alanın XML özelliÄŸi olarak tanımlanmasına yarıyor. ÖrneÄŸin, eÄŸer id alanımız XStreamAsAttribute olarak tanımlansaydı XML yapımız <user id=”1″> <userName>bhdrkn</userName></user> ÅŸeklinde oluÅŸturulacaktı.
- XStreamOmitField: Bu annotation sınıfı, serileştirmek istemediğiniz alanları belirtmenizde kullanılıyor. Örneğin sınıfınız içerisinde sabit alanlar varsa bunların serileştirilmesini bu annotation sınıfını kullanarak engelleyebilirsiniz.
- XStreamImplicit: Bu annotation sınıfı, liste veya dizilerinizi serileştirmek için kullanılıyor.
[/fancy_list]
XStream Factory Sınıfı
Bu işlemi önceki XStream yapısında da yapmıştık. Fakat şimdi daha generic olarak tekrarlayalım. Belirtmekte fayda var tek değişen kısım, XStream nesnenizin ayarlanması olacaktır. Generikliği sağlamak adına, IXmlFactory ve AbstractXmlFactory sınıfları oluşturulmuştur.
[toggle title=”IXmlFactory.java” variation=”blue”]
public interface IXmlFactory extends Serializable{ void toXmlFile(T t, String filePath); T fromXmlFile(String filePath); T fromXmlString(String xmlString); String toXmlString(T t); }
[/toggle]
[toggle title=”AbstractXmlFactory.java” variation=”blue”]
public abstract class AbstractXmlFactory implements IXmlFactory { private static final long serialVersionUID = 1L; protected XStream xStream = null; public AbstractXmlFactory() { super(); xStream = new XStream(new DomDriver()); xStream.processAnnotations(((Class) ((ParameterizedType) getClass() .getGenericSuperclass()).getActualTypeArguments()[0])); xStream.setMode(XStream.XPATH_ABSOLUTE_REFERENCES); } public void toXmlFile(final T t, final String filePath) { try { File file = new File(filePath); if (!file.exists()) { file.createNewFile(); } BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter( file)); xStream.toXML(t, bufferedWriter); } catch (Exception e) { System.err.println("Exception in toXmlFile M:" + e.getMessage() + " C: " + e.getCause() + " ST: " + e.getStackTrace()); } } public T fromXmlFile(final String filePath) { T t = null; try { File file = new File(filePath); BufferedReader bufferedReader = new BufferedReader(new FileReader( file)); t = ((T) this.xStream.fromXML(bufferedReader)); } catch (Exception e) { System.err.println("Exception in fromXmlFile M:" + e.getMessage() + " C: " + e.getCause() + " ST: " + e.getStackTrace()); } return t; } public String toXmlString(final T t) { return this.xStream.toXML(t); } public T fromXmlString(String xmlString) { T t = null; t = ((T) this.xStream.fromXML(xmlString)); return t; } }
[/toggle]
Generic sınıflarımızı oluşturduktan sonra tek yapmamız gereken, her model sınıfımız için ilgili factory sınıfını oluşturmak. Bunu kodunuzun içerisinde kullanım sırasında yapabileceğiniz gibi, sınıfı doğrudan da oluşturabilirsiniz. Ben doğrudan oluşturmayı seçip, UserXmlFactory sınıfını yazdım.
UserXmlFactory.java
public class UserXmlFactory extends AbstractXmlFactory<User> { private static final long serialVersionUID = 1L; public static final String USER_XML_FILE = "user.xml"; public UserXmlFactory() { super(); } }
Test
Son olarak yazılan kodun test edilmesi kalıyor. Bunun için projenin oluşturulmasıyla yaratılmış App sınıfını kullanabiliriz. Test için App sınıfını aşağıdaki gibi değiştiriyoruz.
App.java
public class App { public static void main(String[] args) { App app = new App(); System.out.println("XSTREAM TEST APP STARTED"); app.createUserXml(); System.out.println("*** USER XML FILE CREATED"); User user = app.readFromUserXml(); System.out.println("*** USER XML FILE READ: " + user.toString()); System.out.println("End Of Line"); } public void createUserXml() { User user = new User(1, "bhdrkn"); UserXmlFactory factory = new UserXmlFactory(); factory.toXmlFile(user, UserXmlFactory.USER_XML_FILE); } public User readFromUserXml() { return new UserXmlFactory().fromXmlFile(UserXmlFactory.USER_XML_FILE); } }
Yukarıdaki kodu derleyip çalıştırdığınızda aşağıdaki konsol çıktılarını ve aşağıdaki user.xml dosya çıktısını elde edeceksiniz.
[toggle title=”Konsol” variation=”blue”]
1234 XSTREAM TEST APP STARTED*** USER XML FILE CREATED*** USER XML FILE READ: UserName: bhdrkn with id: 1End Of Line
[/toggle]
[toggle title=”user.xml” variation=”blue”]
<user> <id>1</id> <userName>bhdrkn</userName> </user>
[/toggle]
Kaynaklar
GitHub Adresi: https://github.com/bhdrkn/Java-Examples/tree/master/XStreamAnnotation
End Of Line