1概要

このチュートリアルでは、https://bitbucket.org/asomov/snakeyaml/over[[SnakeYAML]ライブラリを使用して、JavaオブジェクトをYAMLドキュメントにシリアライズし、その逆を行う方法を学びます。


2プロジェクト設定

私たちのプロジェクトでSnakeYAMLを使用するために、以下のMaven依存関係を追加します(最新バージョンはhttps://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.yamlにあります)。 %22%20AND%20a%3A%22 snakeyaml%22[ここ]):

<dependency>
    <groupId>org.yaml</groupId>
    <artifactId>snakeyaml</artifactId>
    <version>1.21</version>
</dependency>


3エントリーポイント


Yaml

クラスはAPIのエントリポイントです。

Yaml yaml = new Yaml();

実装はスレッドセーフではないため、異なるスレッドはそれぞれ独自の

Yaml

インスタンスを持たなければなりません。


4 YAMLドキュメントを読み込む

このライブラリは、

String

または

InputStream

から文書をロードするためのサポートを提供します。このコードサンプルの大部分は、

InputStream

の解析に基づいています。

簡単なYAMLドキュメントを定義し、ファイルに

customer.yaml

という名前を付けることから始めましょう。

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

** 4.1. 基本的な使い方

それでは、上記のYAMLドキュメントを

Yaml

クラスで解析します。

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

上記のコードは以下の出力を生成します。

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

デフォルトでは、

load()

メソッドは

Map

インスタンスを返します。毎回

Map

オブジェクトを照会するには、事前にプロパティキーの名前を知っておく必要があります。また、入れ子になったプロパティを検索することも容易ではありません。

** 4.2. カスタムタイプ

このライブラリは、ドキュメントをカスタムクラスとしてロードする方法も提供します。このオプションにより、メモリ内のデータを簡単に移動できます。


Customer

クラスを定義して、もう一度ドキュメントをロードしてみます。

public class Customer {

    private String firstName;
    private String lastName;
    private int age;

   //getters and setters
}

YAMLドキュメントが既知の型として逆シリアル化されると仮定すると、ドキュメント内で明示的なグローバルタグを指定できます。

ドキュメントを更新して、新しいファイル

customer

with__type.yamlに保存しましょう。

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

ドキュメントの最初の行に注意してください。これには、ロード時に使用されるクラスに関する情報が含まれています。

それでは、上で使用したコードを更新し、新しいファイル名を入力として渡します。

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


load()

メソッドは、

Customer

type


_のインスタンスを返すようになりました。

_

** この方法の欠点は、必要な場所で使用するためにはその型をライブラリとしてエクスポートする必要があることです。

ただし、ライブラリをエクスポートする必要がない明示的なローカルタグを使用することもできます。

  • カスタム型をロードするもう一つの方法は

    Constructor

    クラスを使うことです** 。このようにして私達はパースされるYAMLドキュメントのルートタイプを指定することができます。

    Customer

    型をルート型として

    Constructor

    インスタンスを作成し、それを

    Yaml

    インスタンスに渡します。


__customer.yamlをロードすると、

Customer__オブジェクトが取得されます。

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


4.3. 暗黙の型

  • 特定のプロパティに型が定義されていない場合、ライブラリは自動的に値を暗黙の型に変換します** 。

例えば:

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

テストケースを使用して、この暗黙の型変換をテストしましょう。

@Test
public void whenLoadYAML__thenLoadCorrectImplicitTypes() {
   Yaml yaml = new Yaml();
   Map<Object, Object> document = yaml.load("3.0: 2018-07-22");

   assertNotNull(document);
   assertEquals(1, document.size());
   assertTrue(document.containsKey(3.0d));
}


4.4. 入れ子になったオブジェクトとコレクション

最上位の型が与えられると、ライブラリは自動的に入れ子になったオブジェクトの型を検出します。ただし、それらがインタフェースまたは抽象クラスでない場合は、ドキュメントを適切な入れ子型に逆シリアル化します。


Contact




_Address


detailsを

customer.yamlに追加し、新しいファイルを

customer

with

contact

details

and

address.yamlとして保存します。 _

それでは、新しいYAMLドキュメントを解析しましょう。

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


Customer

クラスもこれらの変更を反映するはずです。これが更新されたクラスです。

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


Contact

クラスと

Address

クラスがどのように見えるかを見てみましょう。

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
}

次に、与えられたテストケースで

Yaml



load()

をテストします。

@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. タイプセーフなコレクション

特定のJavaクラスの1つ以上のプロパティが型保証された(汎用)コレクションの場合、正しいパラメータ化された型が識別されるように

TypeDescription

を指定することが重要です。

1人の

Customer

に複数の

Contact

があるとしましょう。

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

このドキュメントを読み込むために、トップレベルクラスの特定のプロパティに____TypeDescriptionを指定できます。

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. 複数の文書を読み込む

単一の

File

に複数のYAMLドキュメントがあり、それらすべてを解析したい場合があるかもしれません。

Yaml

クラスはそのようなタイプの解析をするための

loadAll()

メソッドを提供します。

デフォルトでは、このメソッドは

Iterable <Object>

のインスタンスを返します。各オブジェクトは


_Map <String、Object>型です。


カスタム型が必要な場合は、上で説明したように

Constructor

インスタンスを使用できます。

_

次の文書を1つのファイルにまとめてください。

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

以下のコードサンプルに示すように、

loadAll()

メソッドを使用して上記を解析できます。

@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 YAML文書をダンプする

ライブラリはまた、与えられたJavaオブジェクトをYAMLドキュメントにダンプするためのメソッドも提供します。出力は

String

または指定されたファイル/ストリームです。

** 5.1. 基本的な使い方


Map <String、Object>

のインスタンスをYAMLドキュメント(

String

)にダンプする簡単な例から始めます。

@Test
public void whenDumpMap__thenGenerateCorrectYAML() {
    Map<String, Object> data = new LinkedHashMap<String, Object>();
    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());
}

上記のコードでは、次のような出力が生成されます(

LinkedHashMap

のインスタンスを使用しても、出力データの順序は保持されます)。

name: Silenthand Olleander
race: Human
traits:[ONE__HAND, ONE__EYE]----

====  **  5.2. カスタムJavaオブジェクト**

カスタムJava型を出力ストリームにダンプすることもできます。

ただし、これにより、出力文書にグローバルな明示的な__tag__が追加されます。

[source,java,gutter:,true]

@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());
}

上記のアプローチでは、まだタグ情報をYAMLドキュメントにダンプしています。

つまり、クラスを逆シリアル化している消費者のためのライブラリとしてクラスをエクスポートする必要があります。出力ファイル内のタグ名を避けるために、ライブラリが提供する__dumpAs()__メソッドを使うことができます。

そのため、上記のコードでは、タグを削除するために以下を微調整できます。

[source,java,gutter:,true]

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

===  **  6. 結論**

この記事では、JavaオブジェクトをYAMLにシリアライズするためのSnakeYAMLライブラリーの使用法、およびその逆の使用法について説明しました。

すべての例はhttps://github.com/eugenp/tutorials/tree/master/libraries[GitHubプロジェクト]にあります - これはMavenベースのプロジェクトなので、そのままインポートして実行するのは簡単なはずです。 。