Edited at

ハッシュのハッシュになっているyamlをsnakeyamlで読み込む時のサンプルソース

More than 1 year has passed since last update.


やりたいこと


  • こういうYAMLをsnakeyamlで読み込みたい。ハッシュのハッシュのかたち。


personData.yaml

yamada:

id: 100
address: 東京
age: 20
tanaka:
id: 101
address: さいたま
age: 22


実現の条件


  • キャストはしたくない。


  • @SuppressWarnings("unchecked") なんてやりたくない。

  • なるべくソースを書きたくない。


  • Yaml.loadAs()とかをうまく使ってなるべく楽したい。


適用の検討

ハッシュなのでjava.util.MapYaml.loadAs()したとする。これだと型パラメータを指定できないのでキャストとか面倒事が増える。

Yaml y = new Yaml();

Map<?, ?> personMap = y.loadAs(PersonDataYamlTest.class.getResourceAsStream("/personMap.yaml"), Map.class);

こんなpersonMapは使いたくない。しかし、型パラメータを直接指定するようなAPIはなさそうなので、別のやり方をとる。

妥協案的に、yamlを以下の形式に変える。


personData2.yaml

personData :

yamada:
id: 100
address: 東京
age: 20
tanaka:
id: 101
address: さいたま
age: 22

読み込むコード


PersonDataYaml.java

package testsnakeyaml;

import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.util.Map;

public class PersonDataYaml {
public Map<String, Person> personData;

static PersonDataYaml loadSetting(String file) {
Constructor constructor = new Constructor(PersonDataYaml.class);
TypeDescription desc = new TypeDescription(PersonDataYaml.class);
desc.putMapPropertyType("personData", String.class, Person.class);
constructor.addTypeDescription(desc);
Yaml y = new Yaml(constructor);
return y.loadAs(PersonDataYaml.class.getResourceAsStream(file), PersonDataYaml.class);
}

static class Person {
public int id;
public String address;
public int age;
}
}


テストコード


PersonDataYamlTest.java

package testsnakeyaml;

import org.junit.jupiter.api.Test;
import org.yaml.snakeyaml.Yaml;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class PersonDataYamlTest {
@Test
void testRawMap() {
Yaml y = new Yaml();
Map<?, ?> personMap = y.loadAs(PersonDataYamlTest.class.getResourceAsStream("/personData.yaml"), Map.class);

assertTrue(personMap.values().iterator().next() instanceof Map);
}

@Test
void testBean() {
PersonDataYaml yaml = PersonDataYaml.loadSetting("/personData2.yaml");
assertEquals(100, yaml.personData.get("yamada").id);
assertEquals("東京", yaml.personData.get("yamada").address);
assertEquals("さいたま", yaml.personData.get("tanaka").address);
}
}



考えたこと


  • 読み込むYAML全体を、PersonDataYaml.javaとし、personDataなるフィールドに型がつけられたMapを保持させる。


    • 階層が1個増えたが妥協なので我慢。



  • ハッシュのキーは、JavaBeansの仕様に合わせる必要があるようなので、ここでは簡単にpublicフィールドとする。


    • 要件次第でsetter,getterを付ける。



  • staticなファクトリメソッドで読み込み処理をやらせる。必要なデータと操作を全て押し込める事ができたようなのでこれで良しとする。

  • もっと簡素に書けるなら、そのように修正したい。


利用したバージョン


  • SnakeYAML 1.17


ネタ元

(このwiki記事と内容一緒なんですが、ベストプラクティス的になってないのでわたしなりのを書いてみた次第。)