Search code examples
javayamlsnakeyaml

Parsing a YAML document with a map at the root using snakeYaml


I want to read a YAML document to a map of custom objects (instead of maps, which snakeYaml does by default). So this:

19:
  typeID: 2
  limit: 300
20:
  typeID: 8
  limit: 100

Would be loaded to a map which looks like this:

Map<Integer, Item>

where Item is:

class Item {
    private Integer typeId;
    private Integer limit;
}

I could not find a way to do this with snakeYaml, and I couldn't find a better library for the task either.

The documentation only has examples with maps/collections nested inside other objects, so that you can do the following:

    TypeDescription typeDescription = new TypeDescription(ClassContainingAMap.class);
    typeDescription.putMapPropertyType("propertyNameOfNestedMap", Integer.class, Item.class);
    Constructor constructor = new Constructor(typeDescription);
    Yaml yaml = new Yaml(constructor);
    /* creating an input stream (is) */
    ClassContainingAMap obj = (ClassContainingAMap) yaml.load(is);

But how do I go about defining the Map format when it is at the root of the document?


Solution

  • You need to add a custom Constructor. However, in your case you don't want register an "item" or "item-list" tag.

    In effect, you want to apply Duck Typing to your Yaml. It's not super efficient, but there is a relatively easy way to do this.

    class YamlConstructor extends Constructor {
      @Override
      protected Object constructObject(Node node) {
    
        if (node.getTag() == Tag.MAP) {
            LinkedHashMap<String, Object> map = (LinkedHashMap<String, Object>) super
                    .constructObject(node);
            // If the map has the typeId and limit attributes
            // return a new Item object using the values from the map
            ...
        }
         // In all other cases, use the default constructObject.
        return super.constructObject(node);