Reputation: 25966
I've been trying to deserialize the following yaml to a List<Stage>
using SnakeYaml:
- name: Stage1
items:
- item1
- item2
- name: Stage2
items:
- item3
public class Stage {
private String name;
private List<String> items;
public Stage() {
}
public Stage(String name, List<String> items) {
this.name = name;
this.items = items;
}
// getters and setters
}
The closest question I found was SnakeYaml Deserialise Class containing a List of Objects.
After reading it, I am aware of Constructor
and TypeDescriptor
classes, but I am still unable to get it working (I get list of HashMaps, not Stages).
The difference with the question in the link above is that my top-level structure is a list, not a custom object.
Upvotes: 11
Views: 4762
Reputation: 1295
If you don't mind bringing in a dependency on Jackson and Jackson Dataformat YAML, this can be achieved in a single line of code, using Jackson's YAMLMapper
in conjunction with a TypeReference
:
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class StageListTest
{
@Test
public void testLoadStage() throws Exception
{
// The YAML input file:
InputStream yaml = getClass().getClassLoader().getResourceAsStream("stagelist.yml");
// The expected result:
List<Stage> expected = Arrays.asList
(
new Stage("Stage1", Arrays.asList("item1", "item2")),
new Stage("Stage2", Arrays.asList("item3"))
);
// Use Jackson YAMLMapper to load file:
List<Stage> result = new YAMLMapper().readValue(yaml, new TypeReference<List<Stage>>() {});
// Make sure result is as expected:
assertEquals(expected, result);
}
}
(on top of getters and setters, hashCode
and equals
methods were added to the Stage
type to support comparison of Stage
objects)
The above solution uses SnakeYAML under the hood, but does not require creation of a custom type constructor.
Upvotes: 1
Reputation: 2280
One way would be to create your own snakeyaml Constructor like this:
public class ListConstructor<T> extends Constructor {
private final Class<T> clazz;
public ListConstructor(final Class<T> clazz) {
this.clazz = clazz;
}
@Override
protected Object constructObject(final Node node) {
if (node instanceof SequenceNode && isRootNode(node)) {
((SequenceNode) node).setListType(clazz);
}
return super.constructObject(node);
}
private boolean isRootNode(final Node node) {
return node.getStartMark().getIndex() == 0;
}
}
and then use it when constructing the Yaml:
final Yaml yaml = new Yaml(new ListConstructor<>(Stage.class));
Upvotes: 8