Search code examples
javayamljavabeanssnakeyaml

SnakeYAML: How to set bean property type for loading


Using SnakeYAML's TypeDescription, I can add type information about Lists and Maps using:

TypeDescription td = new TypeDescription(MyBean.class, "!mybean");
td.putMapPropertyType("mymap", String.class, MyOtherBean.class);
td.putListPropertyType("mylist", MyOtherBean.class);

This way, I can populate Collections with instances of MyOtherBean without having to rely on any other tags:

---
!mybean
mymap:
  foobar: # will map to an instance of MyOtherBean
    otherbeanprop1: foo
    otherbeanprop2: bar
mylist: # will contain instances of MyOtherBean
  - otherbeanprop1: foo
    otherbeanprop2: bar

Is there any easy way to do something like this:

td.putPropertyType("myotherbean", MyOtherBean.class);

so that the property myotherbean in MyBean would be populated with a single instance of MyOtherBean? Things I am trying to avoid:

  • putting additional tags (like !myotherbean) in the yaml file
  • loading the other beans properties from a Map and constructing the bean myself

I have already played around with this and I think I need to create a custom constructor like this (adapted from the SnakeYAML Documentation):

class SelectiveConstructor extends Constructor {
    public SelectiveConstructor() {
        // define a custom way to create a mapping node
        yamlClassConstructors.put(NodeId.mapping, new ConstructMapping() {
            @Override
            protected Object constructJavaBean2ndStep(MappingNode node, Object object) {
                Class type = node.getType();
                if (type.equals(MyBean.class)) {
                    // something
                    if (propertyname.equals("myotherbean")) {
                        // something
                    }
                } else {
                    // redirect creation to Constructor
                    return super.constructJavaBean2ndStep(node, object);
                }

        }
    }
}

The problem is that I don't really understand what exactly is going on there. Please let me know if there is an easier way. And if there isn't, I would appreciate it if you shared your experience with this.


Solution

  • There is an elegant way to do this.
    If you don't want to do some special tricks with the YAML content, you can use the following two methods of snakeYaml to dump and load yaml content:

    public String dumpAsMap(Object data)              // dump method
    public <T> T loadAs(Reader io, Class<T> type)     // load method
    

    In your scenario,

    dump :

    MyBean bean = new MyBean();
    // init bean...
    String yamlContent = yaml.dumpAsMap(bean);
    

    load :

    FileReader reader = new FileReader(filename);
    MyBean bean = yaml.loadAs(reader, MyBean.class);
    

    Of course, you should implement the getter and setter of your classes. For details, you can take a look at the example here.