[java] SnakeYaml の機能を利用して Java でも YAML の custom tag を利用する
YAML には tag という型を規定する機能がある。 ! が文書固有の型で !! がグローバルな型。 see http://yaml.org/type/
h2o の 2.1 では以下のような設定ファイルが書けるようになるそうです。 !file
がカスタムタグになっていて、設定されているハンドラによってファイルが読み込まれる。
hosts:
"example.com":
listen:
port: 443
ssl: !file default_ssl.conf
paths:
...
"example.org":
listen:
port: 443
ssl:
<<: !file default_ssl.conf
certificate-file: /path/to/example.org.crt
key-file: /path/to/example.org.crt
paths:
...
同じことを jackson-dataformat-yaml でやろうと思ったが、これは無理。issue は上がっている
jackson-dataformat-yaml の元になっている SnakeYaml を直接使えばいけるので、直接使う。
package me.geso.tinyconfig;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.AbstractConstruct;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
public class ImportableConstructor extends SafeConstructor {
private Yaml yaml;
public ImportableConstructor() {
this.yamlConstructors.put(new Tag("!file"), new FileConstruct());
this.yamlConstructors.put(new Tag("!resource"), new ResourceConstruct());
}
public void setYaml(Yaml yaml) {
this.yaml = yaml;
}
public Yaml getYaml() {
if (this.yaml == null) {
throw new IllegalStateException("You must set Yaml object to ImportableConstructor.");
}
return this.yaml;
}
private class FileConstruct extends AbstractConstruct {
@Override
public Object construct(Node nnode) {
org.yaml.snakeyaml.nodes.ScalarNode snode = (org.yaml.snakeyaml.nodes.ScalarNode) nnode;
String fileName = snode.getValue();
try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(fileName))) {
return getYaml().load(bufferedReader);
} catch (IOException e) {
throw new YamlImportFailedException(fileName, snode.getTag(), e);
}
}
}
private class ResourceConstruct extends AbstractConstruct {
@Override
public Object construct(Node nnode) {
org.yaml.snakeyaml.nodes.ScalarNode snode = (org.yaml.snakeyaml.nodes.ScalarNode) nnode;
String resourceName = snode.getValue();
try (InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream(resourceName)) {
return getYaml().load(resourceAsStream);
} catch (IOException e) {
throw new YamlImportFailedException(resourceName, snode.getTag(), e);
}
}
}
public static class YamlImportFailedException extends RuntimeException {
public YamlImportFailedException(String fileName, Tag tag, IOException cause) {
super("Cannot load " + tag.getValue() + " from " + fileName + " : " + cause.getClass().getCanonicalName() + " : " + cause.getMessage(), cause);
}
}
}
利用側はこんな感じ。
this.yaml = new Yaml(importableConstructor);
importableConstructor.setYaml(yaml);
情報が少ないので難儀するが、頑張れば実装できました。
Java なので、!file
と !resource
をじっそうして、!resource
の方は classpath からロードできるようにしているところがオシャレポイントです。
Published: 2016-09-09(Mon) 00:25