diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java index df0df35c5..27fb58a00 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactory.java @@ -78,6 +78,26 @@ public class YAMLFactory extends JsonFactory */ protected final LoaderOptions _loaderOptions; + /** + * Configuration for underlying generator to follow, if specified; + * left as {@code null} for backwards compatibility (which means + * the dumper options are derived based on {@link YAMLGenerator.Feature}s). + *

+ * These {@link YAMLGenerator.Feature}s are ignored if you provide your own DumperOptions: + *

+ *

+ * + * @since 2.14 + */ + protected final DumperOptions _dumperOptions; + /* /********************************************************************** /* Factory construction, configuration @@ -107,6 +127,7 @@ public YAMLFactory(ObjectCodec oc) _version = null; _quotingChecker = StringQuotingChecker.Default.instance(); _loaderOptions = null; + _dumperOptions = null; } /** @@ -120,6 +141,7 @@ public YAMLFactory(YAMLFactory src, ObjectCodec oc) _version = src._version; _quotingChecker = src._quotingChecker; _loaderOptions = src._loaderOptions; + _dumperOptions = src._dumperOptions; } /** @@ -134,6 +156,7 @@ protected YAMLFactory(YAMLFactoryBuilder b) _version = b.yamlVersionToWrite(); _quotingChecker = b.stringQuotingChecker(); _loaderOptions = b.loaderOptions(); + _dumperOptions = b.dumperOptions(); } @Override @@ -504,10 +527,13 @@ protected YAMLParser _createParser(byte[] data, int offset, int len, IOContext c @Override protected YAMLGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { int feats = _yamlGeneratorFeatures; - YAMLGenerator gen = new YAMLGenerator(ctxt, _generatorFeatures, feats, - _quotingChecker, _objectCodec, out, _version); - // any other initializations? No? - return gen; + if (_dumperOptions == null) { + return new YAMLGenerator(ctxt, _generatorFeatures, feats, + _quotingChecker, _objectCodec, out, _version); + } else { + return new YAMLGenerator(ctxt, _generatorFeatures, feats, + _quotingChecker, _objectCodec, out, _dumperOptions); + } } @Override diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactoryBuilder.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactoryBuilder.java index c70ee0f95..cf9f1305c 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactoryBuilder.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLFactoryBuilder.java @@ -36,6 +36,9 @@ public class YAMLFactoryBuilder extends TSFBuilder + * Ignored if you provide your own {@code DumperOptions}. + *

*/ protected DumperOptions.Version _version; @@ -53,6 +56,26 @@ public class YAMLFactoryBuilder extends TSFBuilder + * These {@link YAMLGenerator.Feature}s are ignored if you provide your own DumperOptions: + * + *

+ * + * @since 2.14 + */ + protected DumperOptions _dumperOptions; + /* /********************************************************** /* Life cycle @@ -164,6 +187,31 @@ public YAMLFactoryBuilder loaderOptions(LoaderOptions loaderOptions) { return this; } + /** + * Configuration for underlying generator to follow, if specified; + * left as {@code null} for backwards compatibility (which means + * the dumper options are derived based on {@link YAMLGenerator.Feature}s). + *

+ * These {@link YAMLGenerator.Feature}s are ignored if you provide your own DumperOptions: + *

+ *

+ * + * @param dumperOptions the {@code SnakeYAML} configuration to use when generating YAML + * @return This builder instance, to allow chaining + * @since 2.14 + */ + public YAMLFactoryBuilder dumperOptions(DumperOptions dumperOptions) { + _dumperOptions = dumperOptions; + return this; + } + /* /********************************************************** /* Accessors @@ -201,6 +249,29 @@ public LoaderOptions loaderOptions() { return _loaderOptions; } + /** + * Configuration for underlying generator to follow, if specified; + * left as {@code null} for backwards compatibility (which means + * the dumper options are derived based on {@link YAMLGenerator.Feature}s). + *

+ * These {@link YAMLGenerator.Feature}s are ignored if you provide your own DumperOptions: + *

+ *

+ * + * @return the {@code SnakeYAML} configuration to use when generating YAML + * @since 2.14 + */ + public DumperOptions dumperOptions() { + return _dumperOptions; + } + @Override public YAMLFactory build() { return new YAMLFactory(this); diff --git a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java index c96664d1a..a2be3b064 100644 --- a/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java +++ b/yaml/src/main/java/com/fasterxml/jackson/dataformat/yaml/YAMLGenerator.java @@ -58,15 +58,22 @@ public enum Feature implements FormatFeature // since 2.9 /** * Do we try to force so-called canonical output or not. + *

+ * Ignored if you provide your own {@code DumperOptions}. + *

*/ CANONICAL_OUTPUT(false), /** * Options passed to SnakeYAML that determines whether longer textual content * gets automatically split into multiple lines or not. - *

- * Feature is enabled by default to conform to SnakeYAML defaults as well as - * backwards compatibility with 2.5 and earlier versions. + *

+ * Feature is enabled by default to conform to SnakeYAML defaults as well as + * backwards compatibility with 2.5 and earlier versions. + *

+ *

+ * Ignored if you provide your own {@code DumperOptions}. + *

* * @since 2.6 */ @@ -75,10 +82,11 @@ public enum Feature implements FormatFeature // since 2.9 /** * Whether strings will be rendered without quotes (true) or * with quotes (false, default). - *

- * Minimized quote usage makes for more human readable output; however, content is - * limited to printable characters according to the rules of - * literal block style. + *

+ * Minimized quote usage makes for more human readable output; however, content is + * limited to printable characters according to the rules of + * literal block style. + *

* * @since 2.7 */ @@ -87,10 +95,11 @@ public enum Feature implements FormatFeature // since 2.9 /** * Whether numbers stored as strings will be rendered with quotes (true) or * without quotes (false, default) when MINIMIZE_QUOTES is enabled. - *

- * Minimized quote usage makes for more human readable output; however, content is - * limited to printable characters according to the rules of - * literal block style. + *

+ * Minimized quote usage makes for more human readable output; however, content is + * limited to printable characters according to the rules of + * literal block style. + *

* * @since 2.8.2 */ @@ -101,8 +110,9 @@ public enum Feature implements FormatFeature // since 2.9 * literal block style * should be used. This automatically enabled when {@link #MINIMIZE_QUOTES} is set. *

- * The content of such strings is limited to printable characters according to the rules of - * literal block style. + * The content of such strings is limited to printable characters according to the rules of + * literal block style. + *

* * @since 2.9 */ @@ -111,8 +121,12 @@ public enum Feature implements FormatFeature // since 2.9 /** * Feature enabling of which adds indentation for array entry generation * (default indentation being 2 spaces). - *

- * Default value is {@code false} for backwards compatibility + *

+ * Default value is {@code false} for backwards compatibility + *

+ *

+ * Ignored if you provide your own {@code DumperOptions}. + *

* * @since 2.9 */ @@ -120,8 +134,12 @@ public enum Feature implements FormatFeature // since 2.9 /** * Feature enabling of which adds indentation with indicator for array entry generation * (default indentation being 2 spaces). - *

- * Default value is {@code false} for backwards compatibility + *

+ * Default value is {@code false} for backwards compatibility + *

+ *

+ * Ignored if you provide your own {@code DumperOptions}. + *

* * @since 2.12 */ @@ -132,7 +150,11 @@ public enum Feature implements FormatFeature // since 2.9 * serialization should be same as what the default is for current platform. * If disabled, Unix linefeed ({@code \n}) will be used. *

- * Default value is {@code false} for backwards compatibility. + * Default value is {@code false} for backwards compatibility + *

+ *

+ * Ignored if you provide your own {@code DumperOptions}. + *

* * @since 2.9.6 */ @@ -144,8 +166,12 @@ public enum Feature implements FormatFeature // since 2.9 * If disabled, the max key length is left as 128 characters: longer names * are truncated. If enabled, limit is raised to 1024 characters. *

- * Default value is {@code false} for backwards-compatibility (same as behavior - * before this feature was added). + * Default value is {@code false} for backwards-compatibility (same as behavior + * before this feature was added). + *

+ *

+ * Ignored if you provide your own {@code DumperOptions}. + *

* * @since 2.14 */ @@ -283,6 +309,30 @@ public YAMLGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures, _emitStartDocument(); } + /** + * @since 2.14 + */ + public YAMLGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures, + StringQuotingChecker quotingChecker, + ObjectCodec codec, Writer out, + org.yaml.snakeyaml.DumperOptions dumperOptions) + throws IOException + { + super(jsonFeatures, codec); + _ioContext = ctxt; + _formatFeatures = yamlFeatures; + _quotingChecker = (quotingChecker == null) + ? StringQuotingChecker.Default.instance() : quotingChecker; + _writer = out; + _docVersion = dumperOptions.getVersion(); + _outputOptions = dumperOptions; + + _emitter = new Emitter(_writer, _outputOptions); + // should we start output now, or try to defer? + _emit(new StreamStartEvent(null, null)); + _emitStartDocument(); + } + @Deprecated // since 2.12 public YAMLGenerator(IOContext ctxt, int jsonFeatures, int yamlFeatures, ObjectCodec codec, Writer out, diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/CustomNodeStyleTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/CustomNodeStyleTest.java new file mode 100644 index 000000000..04c892d72 --- /dev/null +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/CustomNodeStyleTest.java @@ -0,0 +1,52 @@ +package com.fasterxml.jackson.dataformat.yaml.ser; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.ModuleTestBase; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import org.yaml.snakeyaml.DumperOptions; + +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; + +public class CustomNodeStyleTest extends ModuleTestBase { + + private final ObjectMapper REGULAR_MAPPER = createMapper(null); + private final ObjectMapper BLOCK_STYLE_MAPPER = createMapper(DumperOptions.FlowStyle.BLOCK); + private final ObjectMapper FLOW_STYLE_MAPPER = createMapper(DumperOptions.FlowStyle.FLOW); + + public void testFlowStyles() throws Exception { + // list + assertEquals("key_default:\n- value", + _asYaml(REGULAR_MAPPER, singletonMap("key_default", singletonList("value")))); + assertEquals("{key_flow: [value]}", + _asYaml(FLOW_STYLE_MAPPER, singletonMap("key_flow", singletonList("value")))); + assertEquals("key_block:\n- value", + _asYaml(BLOCK_STYLE_MAPPER, singletonMap("key_block", singletonList("value")))); + + // object + assertEquals("key_default:\n foo: bar", + _asYaml(REGULAR_MAPPER, singletonMap("key_default", singletonMap("foo", "bar")))); + assertEquals("{key_flow: {foo: bar}}", + _asYaml(FLOW_STYLE_MAPPER, singletonMap("key_flow", singletonMap("foo", "bar")))); + assertEquals("key_block:\n foo: bar", + _asYaml(BLOCK_STYLE_MAPPER, singletonMap("key_block", singletonMap("foo", "bar")))); + } + + private String _asYaml(ObjectMapper mapper, Object value) throws Exception { + return mapper.writeValueAsString(value).trim(); + } + + private ObjectMapper createMapper(DumperOptions.FlowStyle flowStyle) { + DumperOptions dumperOptions = new DumperOptions(); + if (flowStyle != null) { + dumperOptions.setDefaultFlowStyle(flowStyle); + } + YAMLFactory yamlFactory = YAMLFactory.builder().dumperOptions(dumperOptions).build(); + return YAMLMapper.builder(yamlFactory) + .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES) + .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER) + .build(); + } +}