From 2abe0fa98b9187f90002ab251880432101daa033 Mon Sep 17 00:00:00 2001 From: Timo Rohrberg Date: Thu, 12 Mar 2020 22:22:56 +0100 Subject: [PATCH] FIX #180: YAML Generator now quotes strings containing special chars When using the YAMLGenerator in the MINIMIZE_QUOTES mode, the generator serializes strings in plain style, i.e. without quoting them. However, as stated in the YAML Spec [1] such plain style strings cannot contain some special characters such as ':', ',', and others. [1] https://yaml.org/spec/1.2/spec.html#id2788859 As with the serialization of boolean-like strings, the YAMLGenerator should be adjusted to always quote strings containing those special characters. I will shortly try to provide a pull request to fix this issue. --- .../dataformat/yaml/YAMLGenerator.java | 20 ++++++++- .../yaml/ser/GeneratorWithMinimizeTest.java | 44 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) 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 8ded2e16..186b32a1 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 @@ -196,6 +196,14 @@ private Feature(boolean defaultState) { "on", "On", "ON", "off", "Off", "OFF", "null", "Null", "NULL" )); + + /** + * As per YAML Plain Styleunquoted strings are + * restriced to a reduced charset and must be quoted in case they contain one of the following characters. + */ + private final static Set SPECIAL_CHARS = new HashSet<>(Arrays.asList( + ":", "#", "[", "]", "{", "}", "," + )); /* /********************************************************** @@ -480,7 +488,7 @@ public void close() throws IOException if (!isClosed()) { // 11-Dec-2019, tatu: Should perhaps check if content is to be auto-closed... // but need END_DOCUMENT regardless - + _emitEndDocument(); _emit(new StreamEndEvent(null, null)); super.close(); @@ -981,6 +989,16 @@ private boolean _valueNeedsQuoting(String name) { case 'Y': // Y/Yes/YES return MUST_QUOTE_VALUES.contains(name); } + return stringContainsItemFromList(name, SPECIAL_CHARS.toArray(new String[]{})); + } + + private static boolean stringContainsItemFromList(String inputStr, String[] items) { + for(int i =0; i < items.length; i++) { + if (inputStr.contains( items[i] )) { + return true; + } + } + return false; } diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/GeneratorWithMinimizeTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/GeneratorWithMinimizeTest.java index 948e9745..f84ea202 100644 --- a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/GeneratorWithMinimizeTest.java +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/ser/GeneratorWithMinimizeTest.java @@ -91,6 +91,50 @@ public void testMinimizeQuotesWithNulls() throws Exception "key: nuLL", yaml); } + public void testMinimizeQuotesWithStringsContainingSpecialChars() throws Exception { + Map content = new HashMap(); + content.put("key", "a:b"); + String yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a:b\"", yaml); + + content.clear(); + content.put("key", "a#b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a#b\"", yaml); + + content.clear(); + content.put("key", "a[b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a[b\"", yaml); + + content.clear(); + content.put("key", "a]b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a]b\"", yaml); + + content.clear(); + content.put("key", "a{b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a{b\"", yaml); + + content.clear(); + content.put("key", "a}b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a}b\"", yaml); + + content.clear(); + content.put("key", "a,b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: \"a,b\"", yaml); + } + public void testLiteralStringsMultiLine() throws Exception { Map content = new HashMap();