1

Groovy 3.0 has a new YamlBuilder class that works in a similar way to the existing JsonBuilder class.

I'm trying to work out if I can use YamlBuilder to generate a literal field in YAML such as:

data: |
  this is
  a literal
  text value

My first guess was Groovy's multiline string would work:

new YamlBuilder() {
  data: '''\
this is
a literal
text value'''
}

But that gives me:

data: "this is\na literal\ntext value\n"`

I don't see anything useful in the YamlBuilder Javadoc and mrhaki's example doesn't show this use-case.

Does anyone know if/how this can be done?

SteveD
  • 5,396
  • 24
  • 33

2 Answers2

3

You can do the following:

import groovy.yaml.*
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import static com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature.LITERAL_BLOCK_STYLE

def yaml = new YamlBuilder()

yaml {
  data '''\
this is
a literal
text value'''
}

println new ObjectMapper(new YAMLFactory().configure(LITERAL_BLOCK_STYLE, true)).writeValueAsString(yaml)

If you need your own custom serialization

tim_yates
  • 167,322
  • 27
  • 342
  • 338
  • This is the better solution than copying and modifying the YamlBuilder/YamlConverter classes. – SteveD Feb 25 '20 at 13:08
  • Yours was pretty sweet though :-) (and also correct) – tim_yates Feb 25 '20 at 13:21
  • Unfortunately, as soon as you have a more complex YAML, the output likely won't match expectations. There's probably a bunch of additional ObjectMapper settings need to be added to match Groovy's YamlBuilder output, but I don't have time to investigate at the moment. – SteveD Mar 03 '20 at 08:14
  • If you paste an example and the expectation into the question, I'll try and take a look – tim_yates Mar 03 '20 at 10:25
  • 1
    @tim_yates nested builders -> yaml nodes -> linkedhashmaps cause stackoverflow I suppose. I'm struggling with this problem as well. – Anton Hlinisty Aug 04 '20 at 16:19
2

Under the covers Groovy's YamlBuilder is using Jackson's JSON to YAML converter.

Jackson's converter does support literal block style, but this needs to be enabled. The current version of YamlBuilder does not support setting options.

I copied the YamlBuilder class and the related YamlConverter class so I could modify the settings.

In the YamlBuilder class, I modified this method:

public static String convertJsonToYaml(Reader jsonReader) {
    try (Reader reader = jsonReader) {
        JsonNode json = new ObjectMapper().readTree(reader);

        return new YAMLMapper().writeValueAsString(json);
    } catch (IOException e) {
        throw new YamlRuntimeException(e);
    }
}

To be this:

public static String convertJsonToYaml(Reader jsonReader) {
    try (Reader reader = jsonReader) {
        JsonNode json = new ObjectMapper().readTree(reader);

        YAMLMapper mapper = new YAMLMapper()
        mapper.configure(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE, true)
        return mapper.writeValueAsString(json);
    } catch (IOException e) {
        throw new YamlRuntimeException(e);
    }
}

This allows me to do:

mapper.configure(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE, true)

Which will successfully render the YAML as a literal block:

data: |-
  this is
  a literal
  text value
SteveD
  • 5,396
  • 24
  • 33
  • I've accepted Tim Yates answer as it is a cleaner solution, but will leave this answer here as an alternative. – SteveD Feb 25 '20 at 13:09