Search code examples
javagroovyyamlsnakeyaml

Snakeyaml appears to unnecessarily wrap simple values in lists


I am attempting to parse the following YAML file using Groovy and Snakeyaml (clearly I have sanitised the data but it is sufficient to demonstrate the issue):

---
info:
  summary: Snakeyaml Issue
examples:
  - 1st example:
      name: Example 1
      sublist:
        - 0.1:
           foo: bar

I would expect the following statements:

println resource.info.summary
println resource.examples."1st example".name
println resource.examples."1st example".sublist."0.1"

to yield:

Snakeyaml Issue
Example 1
[foo:bar]

and:

println resource.examples."1st example".sublist."0.1".foo

to yield:

bar

However, the actual output from:

println resource.info.summary
println resource.examples."1st example".name
println resource.examples."1st example".sublist."0.1"
println resource.examples."1st example".sublist."0.1".foo

is:

Snakeyaml Issue
[Example 1]
[[[foo:bar]]]
[[bar]]

I can only get the desired output by including the list indicies:

println resource.info.summary
println resource.examples[0]."1st example".name
println resource.examples[0]."1st example".sublist[0]."0.1"
println resource.examples[0]."1st example".sublist[0]."0.1".foo

which seems unnecessary. Perhaps I am misunderstanding the structure of the data?

For completeness, the groovy code that I am using to illustrate the issue is shown below (I grabbed the CustomerResolver code off the web so that it would keep floats as strings):

import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor
import org.yaml.snakeyaml.nodes.Tag
import org.yaml.snakeyaml.representer.Representer
import org.yaml.snakeyaml.resolver.Resolver

def fileName = "example.yaml"
def Yaml yaml = new Yaml(new Constructor(), new Representer(), new DumperOptions(),
    new CustomResolver())
def resource = yaml.load(new File(fileName).newInputStream())

println resource.info.summary
println resource.examples."1st example".name
println resource.examples."1st example".sublist."0.1"
println resource.examples."1st example".sublist."0.1".foo

class CustomResolver extends Resolver {

    /*
     * Do not resolve float and timestamp
     */

    protected void addImplicitResolvers() {
        addImplicitResolver(Tag.BOOL, BOOL, "yYnNtTfFoO");
        addImplicitResolver(Tag.INT, INT, "-+0123456789");
        addImplicitResolver(Tag.MERGE, MERGE, "<");
        addImplicitResolver(Tag.NULL, NULL, "~nN\0");
        addImplicitResolver(Tag.NULL, EMPTY, null);

    }
}

Any ideas?


Solution

  • the problem in a way how you access the yaml

    the examples first contains list and only then contains object with key 1st example

    for your case try this access:

    println resource.info.summary
    println resource.examples[0]."1st example"
    println resource.examples[0]."1st example".name
    println resource.examples[0]."1st example".sublist[0]
    println resource.examples[0]."1st example".sublist[0]."0.1"
    println resource.examples[0]."1st example".sublist[0]."0.1".foo
    

    to understand how list accessor works check this example:

    @Grab(group='org.yaml', module='snakeyaml', version='1.18')
    import org.yaml.snakeyaml.Yaml
    
    def Yaml yaml = new Yaml()
    def resource = yaml.load(new StringReader('''---
    info:
      summary: Snakeyaml Issue
    examples:
      - 1st example:
          name: Example 1
      - 1st example:
          name: Example 2
      - 1st example:
          xname: Example 3
      - 2nd example:
          name: Example 4
    '''))
    
    println resource.examples."1st example"
    //prints>  [[name:Example 1], [name:Example 2], [xname:Example 3], null]
    
    println resource.examples."1st example".name
    //prints> [Example 1, Example 2, null]