Panduan Pengguna XStream: Menukar Objek ke XML

1. Gambaran keseluruhan

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

2. Ciri-ciri

Terdapat beberapa faedah menarik untuk menggunakan XStream untuk membuat siri dan mendeserisasi XML:

  • Dikonfigurasi dengan betul, ia menghasilkan XML yang sangat bersih
  • Memberi peluang penting untuk penyesuaian output XML
  • Sokongan untuk grafik objek , termasuk rujukan pekeliling
  • Untuk kebanyakan kes penggunaan, instance XStream selamat untuk thread, setelah dikonfigurasikan (terdapat peringatan ketika menggunakan anotasi)
  • Mesej yang jelas diberikan semasa pengendalian pengecualian untuk membantu mendiagnosis masalah
  • Bermula dengan versi 1.4.7, kami mempunyai ciri keselamatan yang tersedia untuk tidak membenarkan siriisasi jenis tertentu

3. Penyediaan Projek

Untuk menggunakan XStream dalam projek kami, kami akan menambahkan ketergantungan Maven berikut:

 com.thoughtworks.xstream xstream 1.4.9 

4. Penggunaan Asas

The Xstream kelas adalah topeng untuk API. Semasa membuat contoh XStream , kita juga perlu menangani masalah keselamatan benang:

XStream xstream = new XStream();

Sebaik sahaja satu contoh dibuat dan dikonfigurasi, ia boleh dibagikan di beberapa utas untuk marshalling / unmarshalling kecuali anda mengaktifkan pemprosesan anotasi.

4.1. Pemandu

Beberapa pemacu disokong, seperti DomDriver , StaxDriver , XppDriver , dan banyak lagi. Pemacu ini mempunyai ciri prestasi dan penggunaan sumber yang berbeza.

Pemacu XPP3 digunakan secara lalai, tetapi tentu saja kita boleh menukar pemacu dengan mudah:

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

4.2. Menjana XML

Mari mulakan dengan menentukan POJO ringkas untuk - Pelanggan :

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

Sekarang mari kita buat perwakilan objek XML:

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

Dengan menggunakan tetapan lalai, output berikut dihasilkan:

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

Dari keluaran ini, kita dapat melihat dengan jelas bahawa tag yang mengandungi menggunakan nama kelas Pelanggan yang memenuhi syarat secara lalai .

Ada banyak sebab kita mungkin memutuskan bahawa tingkah laku lalai tidak sesuai dengan keperluan kita. Sebagai contoh, kami mungkin tidak selesa mendedahkan struktur pakej aplikasi kami. Juga, XML yang dihasilkan jauh lebih lama.

5. Alias

An alias adalah nama yang kita mahu gunakan untuk unsur-unsur daripada menggunakan nama lalai.

Contohnya, kita boleh menggantikan pelanggan com.baeldung.pojo.Customer dengan pelanggan dengan mendaftarkan alias untuk kelas Pelanggan . Kami juga boleh menambahkan alias untuk sifat kelas. Dengan menggunakan alias, kita dapat menjadikan output XML kita lebih mudah dibaca dan kurang khusus Java.

5.1. Alias ​​Kelas

Alias ​​boleh didaftarkan sama ada secara terprogram atau menggunakan anotasi.

Mari sekarang beri komen kelas Pelanggan kami dengan @XStreamAlias :

@XStreamAlias("customer")

Sekarang kita perlu mengkonfigurasi contoh kita untuk menggunakan anotasi ini:

xstream.processAnnotations(Customer.class);

Sebagai alternatif, jika kita ingin mengkonfigurasi alias secara terprogram, kita boleh menggunakan kod di bawah:

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

Sama ada menggunakan alias atau konfigurasi terprogram, output untuk objek Pelanggan akan jauh lebih bersih:

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

5.2. Alias ​​Bidang

Kami juga dapat menambahkan alias untuk bidang menggunakan anotasi yang sama yang digunakan untuk kelas aliasing. Sebagai contoh, jika kita mahu bidang firstName diganti dengan fn dalam perwakilan XML, kita boleh menggunakan penjelasan berikut:

@XStreamAlias("fn") private String firstName;

Sebagai alternatif, kita dapat mencapai matlamat yang sama secara terprogram:

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

The aliasField kaedah menerima tiga hujah: alias kami ingin digunakan, kelas di mana harta yang telah ditetapkan, dan nama harta yang kita ingin alias.

Mana-mana kaedah yang digunakan outputnya sama:

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

5.3. Alias ​​Lalai

Terdapat beberapa alias yang telah didaftarkan untuk kelas - berikut adalah beberapa perkara berikut:

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 senarai ContactDetails di dalam kelas Pelanggan .

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.