Panduan Pengguna XStream: Mengonversi Objek ke XML

1. Ikhtisar

Dalam tutorial ini, kita akan belajar bagaimana menggunakan perpustakaan XStream untuk membuat serial objek Java ke XML.

2. Fitur

Ada beberapa manfaat menarik menggunakan XStream untuk membuat serial dan deserialisasi XML:

  • Dikonfigurasi dengan benar, ini menghasilkan XML yang sangat bersih
  • Memberikan peluang signifikan untuk penyesuaian keluaran XML
  • Dukungan untuk grafik objek , termasuk referensi melingkar
  • Untuk sebagian besar kasus penggunaan, instance XStream aman untuk thread, setelah dikonfigurasi (ada peringatan saat menggunakan anotasi)
  • Pesan yang jelas disediakan selama penanganan pengecualian untuk membantu mendiagnosis masalah
  • Dimulai dengan versi 1.4.7, kami memiliki fitur keamanan yang tersedia untuk melarang serialisasi jenis tertentu

3. Pengaturan Proyek

Untuk menggunakan XStream dalam proyek kami, kami akan menambahkan dependensi Maven berikut:

 com.thoughtworks.xstream xstream 1.4.9 

4. Penggunaan Dasar

Kelas XStream adalah fasad untuk API. Saat membuat instance XStream , kita perlu menangani masalah keamanan thread juga:

XStream xstream = new XStream();

Setelah instance dibuat dan dikonfigurasi, itu dapat dibagikan di beberapa utas untuk marshalling / unmarshalling kecuali Anda mengaktifkan pemrosesan anotasi.

4.1. Pengemudi

Beberapa driver didukung, seperti DomDriver , StaxDriver , XppDriver , dan lainnya. Driver ini memiliki karakteristik kinerja dan penggunaan sumber daya yang berbeda.

Driver XPP3 digunakan secara default, tetapi tentu saja kita dapat dengan mudah mengubah driver:

XStream xstream = new XStream(new StaxDriver()); 

4.2. Menghasilkan XML

Mari kita mulai dengan mendefinisikan POJO sederhana untuk - Pelanggan :

public class Customer { private String firstName; private String lastName; private Date dob; // standard constructor, setters, and getters }

Sekarang mari kita buat representasi XML dari objek:

Customer customer = new Customer("John", "Doe", new Date()); String dataXml = xstream.toXML(customer);

Menggunakan pengaturan default, output berikut ini dihasilkan:

 John Doe 1986-02-14 03:46:16.381 UTC  

Dari keluaran ini, kita dapat dengan jelas melihat bahwa tag yang mengandung menggunakan nama kelas Pelanggan yang memenuhi syarat secara default .

Ada banyak alasan mengapa kita mungkin memutuskan bahwa perilaku default tidak sesuai dengan kebutuhan kita. Misalnya, kita mungkin tidak nyaman memperlihatkan struktur paket aplikasi kita. Selain itu, XML yang dihasilkan jauh lebih panjang.

5. Alias

Sebuah alias adalah nama kita ingin menggunakan untuk elemen daripada menggunakan nama default.

Misalnya, kita dapat mengganti com.baeldung.pojo.Customer dengan customer dengan mendaftarkan alias untuk kelas Customer . Kami juga dapat menambahkan alias untuk properti kelas. Dengan menggunakan alias, kita bisa membuat keluaran XML kita lebih mudah dibaca dan tidak terlalu spesifik untuk Java.

5.1. Alias ​​Kelas

Alias ​​dapat didaftarkan baik secara terprogram atau menggunakan anotasi.

Sekarang mari kita beri anotasi kelas Pelanggan kita dengan @XStreamAlias :

@XStreamAlias("customer")

Sekarang kita perlu mengkonfigurasi instance kita untuk menggunakan anotasi ini:

xstream.processAnnotations(Customer.class);

Sebagai alternatif, jika kita ingin mengkonfigurasi sebuah alias secara terprogram, kita dapat menggunakan kode di bawah ini:

xstream.alias("customer", Customer.class);

Baik menggunakan konfigurasi alias atau terprogram, keluaran untuk objek Pelanggan akan jauh lebih bersih:

 John Doe 1986-02-14 03:46:16.381 UTC  

5.2. Alias ​​Bidang

Kita juga dapat menambahkan alias untuk bidang menggunakan anotasi yang sama yang digunakan untuk kelas aliasing. Misalnya, jika kita ingin field firstName diganti dengan fn dalam representasi XML, kita bisa menggunakan anotasi berikut:

@XStreamAlias("fn") private String firstName;

Atau, kita dapat mencapai tujuan yang sama secara terprogram:

xstream.aliasField("fn", Customer.class, "firstName");

The aliasField Metode menerima tiga argumen: alias kita ingin digunakan, kelas di mana properti didefinisikan, dan nama properti kami ingin alias.

Metode mana pun yang digunakan, hasilnya sama:

 John Doe 1986-02-14 03:46:16.381 UTC 

5.3. Alias ​​Default

Ada beberapa alias yang didaftarkan sebelumnya untuk kelas - berikut beberapa di antaranya:

alias("float", Float.class); alias("date", Date.class); alias("gregorian-calendar", Calendar.class); alias("url", URL.class); alias("list", List.class); alias("locale", Locale.class); alias("currency", Currency.class);

6. Koleksi

Sekarang kita akan menambahkan daftar ContactDetails di dalam kelas Customer .

private List contactDetailsList;

With default settings for collection handling, this is the output:

 John Doe 1986-02-14 04:14:05.874 UTC   6673543265 0124-2460311   4676543565 0120-223312   

Let's suppose we need to omit the contactDetailsList parent tags, and we just want each ContactDetails element to be a child of the customer element. Let us modify our example again:

xstream.addImplicitCollection(Customer.class, "contactDetailsList");

Now, when the XML is generated, the root tags are omitted, resulting in the XML below:

 John Doe 1986-02-14 04:14:20.541 UTC  6673543265 0124-2460311   4676543565 0120-223312  

The same can also be achieved using annotations:

@XStreamImplicit private List contactDetailsList;

7. Converters

XStream uses a map of Converter instances, each with its own conversion strategy. These convert supplied data to a particular format in XML and back again.

In addition to using the default converters, we can modify the defaults or register custom converters.

7.1. Modifying an Existing Converter

Suppose we weren't happy with the way the dob tags were generatedusing the default settings. We can modify the custom converter for Date provided by XStream (DateConverter):

xstream.registerConverter(new DateConverter("dd-MM-yyyy", null));

The above will produce the output in “dd-MM-yyyy” format:

 John Doe 14-02-1986 

7.2. Custom Converters

We can also create a custom converter to accomplish the same output as in the previous section:

public class MyDateConverter implements Converter { private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); @Override public boolean canConvert(Class clazz) { return Date.class.isAssignableFrom(clazz); } @Override public void marshal( Object value, HierarchicalStreamWriter writer, MarshallingContext arg2) { Date date = (Date)value; writer.setValue(formatter.format(date)); } // other methods }

Finally, we register our MyDateConverter class as below:

xstream.registerConverter(new MyDateConverter());

We can also create converters that implement the SingleValueConverter interface, which is designed to convert an object into a string.

public class MySingleValueConverter implements SingleValueConverter { @Override public boolean canConvert(Class clazz) { return Customer.class.isAssignableFrom(clazz); } @Override public String toString(Object obj) { SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); Date date = ((Customer) obj).getDob(); return ((Customer) obj).getFirstName() + "," + ((Customer) obj).getLastName() + "," + formatter.format(date); } // other methods }

Finally, we register MySingleValueConverter:

xstream.registerConverter(new MySingleValueConverter()); 

Using MySingleValueConverter, the XML output for a Customer is as follows:

John,Doe,14-02-1986

7.3. Converter Priority

When registering Converter objects, is is possible to set their priority level, as well.

From the XStream javadocs:

The converters can be registered with an explicit priority. By default they are registered with XStream.PRIORITY_NORMAL. Converters of same priority will be used in the reverse sequence they have been registered. The default converter, i.e. the converter which will be used if no other registered converter is suitable, can be registered with priority XStream.PRIORITY_VERY_LOW. XStream uses by default the ReflectionConverter as the fallback converter.

The API provides several named priority values:

private static final int PRIORITY_NORMAL = 0; private static final int PRIORITY_LOW = -10; private static final int PRIORITY_VERY_LOW = -20; 

8.Omitting Fields

We can omit fields from our generated XML using either annotations or programmatic configuration. In order to omit a field using an annotation, we simply apply the @XStreamOmitField annotation to the field in question:

@XStreamOmitField private String firstName;

In order to omit the field programmatically, we use the following method:

xstream.omitField(Customer.class, "firstName");

Whichever method we select, the output is the same:

 Doe 14-02-1986 

9. Attribute Fields

Sometimes we may wish to serialize a field as an attribute of an element rather than as element itself. Suppose we add a contactType field:

private String contactType;

If we want to set contactType as an XML attribute, we can use the @XStreamAsAttribute annotation:

@XStreamAsAttribute private String contactType; 

Alternatively, we can accomplish the same goal programmatically:

xstream.useAttributeFor(ContactDetails.class, "contactType");

The output of either of the above methods is the same:

 6673543265 0124-2460311 

10. Concurrency

XStream's processing model presents some challenges. Once the instance is configured, it is thread-safe.

It is important to note that processing of annotations modifies the configuration just before marshalling/unmarshalling. And so – if we require the instance to be configured on-the-fly using annotations, it is generally a good idea to use a separate XStream instance for each thread.

11. Conclusion

In this article, we covered the basics of using XStream to convert objects to XML. We also learned about customizations we can use to ensure the XML output meets our needs. Finally, we looked at thread-safety problems with annotations.

In the next article in this series, we will learn about converting XML back to Java objects.

The complete source code for this article can be downloaded from the linked GitHub repository.