I am new to YAML. I have tried to follow a tutorial (https://www.java-success.com/yaml-java-using-snakeyaml-library-tutorial/), but it is incomplete. So far, my YAML file looks like this:
tests:
- class: tests.layers.DeleteCustomLayerTest
param: layer
value: Test Layer
- class: tests.routes.ViewRoutesTest
param: project
value: SNS Project Test
I have added snakeyaml to my pom.xml file:
<dependencies>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.21</version>
</dependency>
I have added snakeyaml-1.21.jar to me libs folder.
I have created a definition for the file called YamlTestDataFile:
package otherObjects;
import java.util.Map;
public class YamlTestDataFile {
private Map<String, String> tests;
public Map<String, String> getTests() {
return tests;
}
public void setTests(Map<String, String> tests) {
this.tests = tests;
}
public Map<String, String> getTests(Map<String, String> tests) {
this.tests = tests;
return tests;
}
}
And finally, a utility to read the file:
package utils;
import java.io.InputStream;
import org.yaml.snakeyaml.Yaml;
import otherObjects.YamlTestDataFile;
public class ReadYamlTestData {
public static void main(String[] args) {
YamlTestDataFile testData = null;
Yaml yaml = new Yaml();
try(InputStream in = ClassLoader.getSystemResourceAsStream("testData.yml")) {
testData = yaml.loadAs(in, YamlTestDataFile.class);
} catch(Exception ex) {
ex.printStackTrace();
}
String test = testData.getTests().get("class");
System.out.println("test=" + test);
}
}
When I run it, I get the following error:
Cannot create property=tests for JavaBean=otherObjects.YamlTestDataFile@5fdef03a
in 'reader', line 1, column 1:
tests:
^
No suitable constructor with 2 arguments found for interface java.util.Map
in 'reader', line 2, column 1:
- class: tests.layers.DeleteCust ...
I am afraid I haven't a clue what is going on, and cannot find any decent tutorials that explain any better.
Following on from flyx's reply, I have made some headway with this.
First, I changed the YAML file so it did not use the word class:
tests:
- className: tests.layers.DeleteCustomLayerTest
param: layer
value: Test Layer
- className: tests.routes.ViewRoutesTest
param: project
value: SNS Project Test
My YAML file definition looks like this:
package otherObjects;
import java.util.List;
public class YamlTestDataFile {
/* public, no-argument constructor */
public YamlTestDataFile() {
}
public static final class Test {
private String className;
public String param;
public String value;
public String getClassName() { return className; }
public void setClassName(final String value) { className = value; }
public String getParam() { return param; }
public void setParam(final String value) { param = value; }
public String getValue() { return value; }
public void setValue(final String string) { param = string; }
}
private List<Test> tests;
public List<Test> getTests() {
return tests;
}
public void setTests(List<Test> tests) {
this.tests = tests;
}
}
and my reader:
package utils;
import java.io.InputStream;
import java.util.List;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import otherObjects.YamlTestDataFile;
import otherObjects.YamlTestDataFile.Test;
public class ReadYamlTestData {
public static void main(String[] args) {
Constructor constructor = new Constructor(YamlTestDataFile.class);
TypeDescription dataDesc = new TypeDescription(YamlTestDataFile.class);
dataDesc.putListPropertyType("tests", YamlTestDataFile.Test.class);
constructor.addTypeDescription(dataDesc);
Yaml yaml = new Yaml(constructor);
InputStream in = ClassLoader.getSystemResourceAsStream("testData.yml");
YamlTestDataFile testData = null;
try {
testData = yaml.loadAs(in, YamlTestDataFile.class);
}catch(Exception e) {
System.err.println("Exception:" + e.getMessage());
e.printStackTrace();
}
List<Test> tests=testData.getTests();
for (int x=0; x<tests.size(); x++)
System.out.println(tests.get(x).getClassName() + ", " + tests.get(x).getParam() + ", " + tests.get(x).getValue());
}
}
The output is pretty much what I am looking for:
tests.layers.DeleteCustomLayerTest, layer, Test Layer
tests.routes.ViewRoutesTest, project, SNS Project Test
The class defined in your code does not match the structure of the YAML data.
In your YAML file, your root is a mapping containing one key, tests
. The value of this key is a sequence of mappings. You map this construct to a class which has a field tests
which is a Map<String, String>
. So you are trying to construct a Java Map
from a YAML sequence. SnakeYaml rightfully complains that it cannot do that.
Your YAML structure looks like you want to do something like this:
public class YamlTestDataFile {
public static final class Test {
private String className;
public String param;
public String value;
public String getClass() { return className; }
public void setClass(final String value) { className = value; }
}
private List<Test> tests;
public List<Test> getTests() {
return tests;
}
public void setTests(List<Test> tests) {
this.tests = tests;
}
}
Note that class
is a keyword in Java, so we need getters and setters to load that value.
Type erasure prevents SnakeYaml to see what the generic parameter for the tests
List is. Therefore, we need to tell it that this is a list of Test
. Let's modify the loading code appropriately:
Constructor constructor = new Constructor(YamlTestDataFile.class);
TypeDescription dataDesc = new TypeDescription(YamlTestDataFile.class);
dataDesc.putListPropertyType("tests", YamlTestDataFile.Test.class);
constructor.addTypeDescription(dataDesc);
Yaml yaml = new Yaml(constructor);
InputStream in = ClassLoader.getSystemResourceAsStream("testData.yml");
testData = yaml.loadAs(in, YamlTestDataFile.class);
Hint: Code untested.