SnakeYAMLを使用したYAMLの解析
1. 概要
このチュートリアルでは、 SnakeYAML ライブラリを使用してオブジェクトをYAMLドキュメントに[X81X]シリアル化する方法、およびその逆の方法を学習します。
2. プロジェクトの設定
プロジェクトでSnakeYAMLを使用するために、次のMaven依存関係を追加します(最新バージョンはここにあります)。
<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ドキュメントが既知のタイプとして逆シリアル化されると仮定すると、ドキュメントで明示的なグローバルtagを指定できます。
ドキュメントを更新して、新しいファイル 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);
The ロード() メソッドは、のインスタンスを返すようになりましたお客様タイプ
ただし、ライブラリをエクスポートする必要のない明示的なローカルタグを使用することもできます。
カスタム型をロードする別の方法は、コンストラクタークラスを使用することです。 このようにして、解析する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. ネストされたオブジェクトとコレクション
最上位の型を指定すると、ライブラリはネストされたオブジェクトの型を自動的に検出します。ただし、それらがインターフェイスまたは抽象クラスでない場合は、ドキュメントを関連するネストされた型に逆シリアル化します。
追加しましょうコンタクトと住所詳細 customer.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つの顧客を取得して、ロードしてみましょう。
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. 複数のドキュメントをロードする
1つのファイルに複数のYAMLドキュメントがあり、それらすべてを解析したい場合があります。 Yaml クラスは、このようなタイプの解析を行うための loadAll()メソッドを提供します。
デフォルトでは、メソッドはのインスタンスを返します反復可能ここで、各オブジェクトはタイプです
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. 基本的な使用法
のインスタンスをダンプする簡単な例から始めましょう地図
@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が出力ドキュメントに追加されます。
@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()メソッドを使用できます。
したがって、上記のコードでは、次のように調整してタグを削除できます。
yaml.dumpAs(customer, Tag.MAP, null);
6. 結論
この記事では、SnakeYAMLライブラリを使用してJavaオブジェクトをYAMLに、またはその逆にシリアル化する方法について説明しました。
すべての例はGitHubプロジェクトにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。