やりたいこと
- こういうYAMLをsnakeyamlで読み込みたい。ハッシュのハッシュのかたち。
personData.yaml
yamada:
id: 100
address: 東京
age: 20
tanaka:
id: 101
address: さいたま
age: 22
実現の条件
- キャストはしたくない。
-
@SuppressWarnings("unchecked")
なんてやりたくない。 - なるべくソースを書きたくない。
-
Yaml.loadAs()
とかをうまく使ってなるべく楽したい。
適用の検討
ハッシュなのでjava.util.Map
でYaml.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
ネタ元
- https://bitbucket.org/asomov/snakeyaml/wiki/Documentation#markdown-header-type-safe-collections
- https://bitbucket.org/asomov/snakeyaml/src/tip/src/test/java/org/yaml/snakeyaml/constructor/TypeSafeCollectionsTest.java?fileviewer=file-view-default
(このwiki記事と内容一緒なんですが、ベストプラクティス的になってないのでわたしなりのを書いてみた次第。)