Menghuraikan YAML dengan SnakeYAML

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan belajar bagaimana menggunakan perpustakaan SnakeYAML untuk menyusun objek Java ke dokumen YAML dan sebaliknya .

2. Penyediaan Projek

Untuk menggunakan SnakeYAML dalam projek kami, kami akan menambahkan ketergantungan Maven berikut (versi terbaru boleh didapati di sini):

 org.yaml snakeyaml 1.21 

3. Tempat Masuk

The YAML kelas adalah pintu masuk untuk API:

Yaml yaml = new Yaml();

Oleh kerana pelaksanaannya tidak selamat untuk benang, utas yang berbeza mesti mempunyai contoh Yaml mereka sendiri .

4. Memuatkan Dokumen YAML

Perpustakaan memberikan sokongan untuk memuatkan dokumen dari String atau InputStream . Sebilangan besar sampel kod di sini didasarkan pada menguraikan InputStream .

Mari mulakan dengan menentukan dokumen YAML yang mudah, dan menamakan fail sebagai pelanggan.yaml :

firstName: "John" lastName: "Doe" age: 20

4.1. Penggunaan Asas

Sekarang kita akan menghuraikan dokumen YAML di atas dengan kelas Yaml :

Yaml yaml = new Yaml(); InputStream inputStream = this.getClass() .getClassLoader() .getResourceAsStream("customer.yaml"); Map obj = yaml.load(inputStream); System.out.println(obj);

Kod di atas menghasilkan output berikut:

{firstName=John, lastName=Doe, age=20}

Secara lalai, kaedah load () mengembalikan contoh Peta . Menanyakan objek Peta setiap kali memerlukan kita mengetahui nama kunci harta tanah terlebih dahulu, dan juga tidak mudah untuk melintasi harta tanah bersarang.

4.2. Jenis Tersuai

Perpustakaan juga menyediakan cara untuk memuatkan dokumen sebagai kelas khusus . Pilihan ini akan memudahkan penyebaran data dalam memori.

Mari tentukan kelas Pelanggan dan cuba memuatkan semula dokumen tersebut:

public class Customer { private String firstName; private String lastName; private int age; // getters and setters }

Dengan mengandaikan dokumen YAML menjadi deserialized sebagai jenis yang diketahui, kita dapat menentukan tag global yang eksplisit dalam dokumen tersebut.

Mari kemas kini dokumen dan simpan dalam fail baru_with_type.yaml:

!!com.baeldung.snakeyaml.Customer firstName: "John" lastName: "Doe" age: 20

Perhatikan baris pertama dalam dokumen, yang memuat maklumat mengenai kelas yang akan digunakan semasa memuatkannya.

Sekarang kita akan mengemas kini kod yang digunakan di atas, dan memberikan nama fail baru sebagai input:

Yaml yaml = new Yaml(); InputStream inputStream = this.getClass() .getClassLoader() .getResourceAsStream("yaml/customer_with_type.yaml"); Customer customer = yaml.load(inputStream); 

Kaedah load () kini mengembalikan contoh jenis Pelanggan . Kelemahan pendekatan ini adalah bahawa jenis tersebut harus dieksport sebagai perpustakaan agar dapat digunakan jika diperlukan .

Walaupun, kami dapat menggunakan tag tempatan yang eksplisit yang kami tidak diminta untuk mengeksport perpustakaan.

Kaedah lain untuk memuat jenis khusus adalah dengan menggunakan kelas Pembina . Dengan cara ini kita dapat menentukan jenis root untuk dokumen YAML yang akan dihuraikan. Marilah kita membuat Constructor contoh dengan Pelanggan jenis sebagai jenis akar dan menyebarkannya kepada YAML misalnya.

Sekarang semasa memuatkan customer.yaml, kami akan mendapat objek Pelanggan :

Yaml yaml = new Yaml(new Constructor(Customer.class));

4.3. Jenis Tersirat

Sekiranya tidak ada jenis yang ditentukan untuk harta benda tertentu, perpustakaan secara automatik menukar nilai menjadi jenis tersirat .

Sebagai contoh:

1.0 -> Float 42 -> Integer 2009-03-30 -> Date

Mari kita uji penukaran jenis tersirat ini menggunakan kes ujian:

@Test public void whenLoadYAML_thenLoadCorrectImplicitTypes() { Yaml yaml = new Yaml(); Map document = yaml.load("3.0: 2018-07-22"); assertNotNull(document); assertEquals(1, document.size()); assertTrue(document.containsKey(3.0d)); }

4.4. Objek dan Koleksi Bersarang

Memandangkan jenis tingkat atas, perpustakaan secara automatik mengesan jenis objek bersarang , kecuali mereka adalah antara muka atau kelas abstrak, dan mendesialisasikan dokumen ke dalam jenis bersarang yang relevan.

Mari tambahkan butiran Kenalan dan Alamat ke customer.yaml, dan simpan fail baru sebagai customer_with_contact_details_and_address.yaml.

Sekarang kita akan menghuraikan dokumen YAML baru:

firstName: "John" lastName: "Doe" age: 31 contactDetails: - type: "mobile" number: 123456789 - type: "landline" number: 456786868 homeAddress: line: "Xyz, DEF Street" city: "City Y" state: "State Y" zip: 345657 

Kelas pelanggan juga harus menunjukkan perubahan ini. Inilah kelas yang dikemas kini:

public class Customer { private String firstName; private String lastName; private int age; private List contactDetails; private Address homeAddress; // getters and setters } 

Mari lihat bagaimana kelas Kenalan dan Alamat :

public class Contact { private String type; private int number; // getters and setters }
public class Address { private String line; private String city; private String state; private Integer zip; // getters and setters }

Sekarang kita akan menguji beban Yaml # () dengan kes ujian yang diberikan:

@Test public void whenLoadYAMLDocumentWithTopLevelClass_thenLoadCorrectJavaObjectWithNestedObjects() { Yaml yaml = new Yaml(new Constructor(Customer.class)); InputStream inputStream = this.getClass() .getClassLoader() .getResourceAsStream("yaml/customer_with_contact_details_and_address.yaml"); Customer customer = yaml.load(inputStream); assertNotNull(customer); assertEquals("John", customer.getFirstName()); assertEquals("Doe", customer.getLastName()); assertEquals(31, customer.getAge()); assertNotNull(customer.getContactDetails()); assertEquals(2, customer.getContactDetails().size()); assertEquals("mobile", customer.getContactDetails() .get(0) .getType()); assertEquals(123456789, customer.getContactDetails() .get(0) .getNumber()); assertEquals("landline", customer.getContactDetails() .get(1) .getType()); assertEquals(456786868, customer.getContactDetails() .get(1) .getNumber()); assertNotNull(customer.getHomeAddress()); assertEquals("Xyz, DEF Street", customer.getHomeAddress() .getLine()); }

4.5. Koleksi Selamat Jenis

Apabila satu atau lebih sifat kelas Java tertentu adalah koleksi jenis selamat (generik), maka penting untuk menentukan Jenis Deskripsi sehingga jenis parameter yang betul dikenal pasti.

Mari ambil satu Pelanggan yang mempunyai lebih dari satu Kenalan , dan cuba memuatkannya:

firstName: "John" lastName: "Doe" age: 31 contactDetails: - { type: "mobile", number: 123456789} - { type: "landline", number: 123456789}

Untuk memuatkan dokumen ini, kita dapat menentukan TypeDescription untuk harta yang diberikan pada kelas tingkat atas :

Constructor constructor = new Constructor(Customer.class); TypeDescription customTypeDescription = new TypeDescription(Customer.class); customTypeDescription.addPropertyParameters("contactDetails", Contact.class); constructor.addTypeDescription(customTypeDescription); Yaml yaml = new Yaml(constructor);

4.6. Memuatkan Banyak Dokumen

Mungkin ada kes di mana, dalam satu Fail terdapat beberapa dokumen YAML, dan kami ingin menguraikan semuanya. The YAML kelas menyediakan LOADALL () kaedah untuk melakukan apa-apa jenis parsing.

By default, the method returns an instance of Iterable where each object is of type Map. If a custom type is desired then we can use the Constructor instance as discussed above.

Consider the following documents in a single file:

--- firstName: "John" lastName: "Doe" age: 20 --- firstName: "Jack" lastName: "Jones" age: 25

We can parse the above using the loadAll() method as shown in the below code sample:

@Test public void whenLoadMultipleYAMLDocuments_thenLoadCorrectJavaObjects() { Yaml yaml = new Yaml(new Constructor(Customer.class)); InputStream inputStream = this.getClass() .getClassLoader() .getResourceAsStream("yaml/customers.yaml"); int count = 0; for (Object object : yaml.loadAll(inputStream)) { count++; assertTrue(object instanceof Customer); } assertEquals(2,count); }

5. Dumping YAML Documents

The library also provides a method to dump a given Java object into a YAML document. The output could be a String or a specified file/stream.

5.1. Basic Usage

We'll start with a simple example of dumping an instance of Map to a YAML document (String):

@Test public void whenDumpMap_thenGenerateCorrectYAML() { Map data = new LinkedHashMap(); data.put("name", "Silenthand Olleander"); data.put("race", "Human"); data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" }); Yaml yaml = new Yaml(); StringWriter writer = new StringWriter(); yaml.dump(data, writer); String expectedYaml = "name: Silenthand Olleander\nrace: Human\ntraits: [ONE_HAND, ONE_EYE]\n"; assertEquals(expectedYaml, writer.toString()); }

The above code produces the following output (note that using an instance of LinkedHashMap preserves the order of the output data):

name: Silenthand Olleander race: Human traits: [ONE_HAND, ONE_EYE]

5.2. Custom Java Objects

We can also choose to dump custom Java types into an output stream. This will, however, add the global explicit tag to the output document:

@Test public void whenDumpACustomType_thenGenerateCorrectYAML() { Customer customer = new Customer(); customer.setAge(45); customer.setFirstName("Greg"); customer.setLastName("McDowell"); Yaml yaml = new Yaml(); StringWriter writer = new StringWriter(); yaml.dump(customer, writer); String expectedYaml = "!!com.baeldung.snakeyaml.Customer {age: 45, contactDetails: null, firstName: Greg,\n homeAddress: null, lastName: McDowell}\n"; assertEquals(expectedYaml, writer.toString()); }

With the above approach, we're still dumping the tag information in YAML document.

This means we have to export our class as a library for any consumer who is deserializing it. In order to avoid the tag name in the output file, we can use the dumpAs() method provided by the library.

So in the above code, we could tweak the following to remove the tag:

yaml.dumpAs(customer, Tag.MAP, null);

6. Conclusion

Artikel ini menggambarkan penggunaan perpustakaan SnakeYAML untuk menyusun objek Java kepada YAML dan sebaliknya.

Semua contoh boleh didapati dalam projek GitHub - ini adalah projek berasaskan Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.