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 a3583a8bd..f397d68a3 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 @@ -175,7 +175,7 @@ private Feature(boolean defaultState) { * aliases for booleans, and we better quote such values as keys; although Jackson * itself has no problems dealing with them, some other tools do have. */ - // 02-Apr-2019, tatu: Some names will look funny if escaped: let's leave out + // 02-Apr-2019, tatu: Some names will look funny if escaped: let's leave out // single letter case (esp so 'y' won't get escaped) private final static Set MUST_QUOTE_NAMES = new HashSet<>(Arrays.asList( // "y", "Y", "n", "N", @@ -217,7 +217,7 @@ private Feature(boolean defaultState) { protected DumperOptions _outputOptions; protected final org.yaml.snakeyaml.DumperOptions.Version _docVersion; - + // for field names, leave out quotes private final static DumperOptions.ScalarStyle STYLE_UNQUOTED_NAME = DumperOptions.ScalarStyle.PLAIN; @@ -964,7 +964,7 @@ private boolean _nameNeedsQuoting(String name) { return true; } return false; - } + } private boolean _valueNeedsQuoting(String name) { switch (name.charAt(0)) { // caller ensures no empty String @@ -990,19 +990,28 @@ private boolean _valueNeedsQuoting(String name) { /** * As per YAML Plain Styleunquoted * strings are restricted to a reduced charset and must be quoted in case they contain - * one of the following characters. + * one of the following characters or character combinations. */ private static boolean _valueHasQuotableChar(String inputStr) { for (int i = 0, end = inputStr.length(); i < end; ++i) { switch (inputStr.charAt(i)) { - case ':': - case '#': case '[': case ']': case '{': case '}': case ',': return true; + case '\t': + case ' ': + if (i < end - 1 && '#' == inputStr.charAt(i + 1)) { + return true; + } + break; + case ':': + if (i < end - 1 && (' ' == inputStr.charAt(i + 1) || '\t' == inputStr.charAt(i + 1))) { + return true; + } + break; default: } } 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 0f8367364..0b6517264 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 @@ -94,15 +94,50 @@ public void testMinimizeQuotesWithNulls() throws Exception public void testMinimizeQuotesWithStringsContainingSpecialChars() throws Exception { Map content; + String yaml = null; + + /* scenarios with plain scalars */ + content = Collections.singletonMap("key", "a:b"); - String yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); assertEquals("---\n" + - "key: \"a:b\"", yaml); + "key: a:b", yaml); content = Collections.singletonMap("key", "a#b"); yaml = MINIM_MAPPER.writeValueAsString(content).trim(); assertEquals("---\n" + - "key: \"a#b\"", yaml); + "key: a#b", yaml); + + content = Collections.singletonMap("key", "a# b"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: a# b", yaml); + + // plus also some edge cases (wrt "false" etc checking + yaml = MINIM_MAPPER.writeValueAsString(Collections.singletonMap("key", "f:off")).trim(); + assertEquals("---\n" + + "key: f:off", yaml); + + + /* scenarios with single quoted scalars */ + + content = Collections.singletonMap("key", "::"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: '::'", yaml); + + content = Collections.singletonMap("key", "#"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: '#'", yaml); + + content = Collections.singletonMap("key", "#a"); + yaml = MINIM_MAPPER.writeValueAsString(content).trim(); + assertEquals("---\n" + + "key: '#a'", yaml); + + + /* scenarios with double quoted scalars */ content = Collections.singletonMap("key", "a[b"); yaml = MINIM_MAPPER.writeValueAsString(content).trim(); @@ -128,10 +163,6 @@ public void testMinimizeQuotesWithStringsContainingSpecialChars() throws Excepti assertEquals("---\n" + "key: \"a,b\"", yaml); - // plus also some edge cases (wrt "false" etc checking - yaml = MINIM_MAPPER.writeValueAsString(Collections.singletonMap("key", "f:off")).trim(); - assertEquals("---\n" + - "key: \"f:off\"", yaml); } public void testLiteralStringsMultiLine() throws Exception @@ -182,7 +213,7 @@ public void testNonQuoteNumberStoredAsString() throws Exception String yaml = MINIM_MAPPER.writeValueAsString(Collections.singletonMap("key", "20")).trim(); assertEquals("---\n" + "key: 20", yaml); - + yaml = MINIM_MAPPER.writeValueAsString(Collections.singletonMap("key", "2.0")).trim(); assertEquals("---\n" + "key: 2.0", yaml); @@ -213,7 +244,7 @@ public void testNumberKey() throws Exception MINIM_MAPPER.writeValueAsString(stringKeyMap).trim()); // And then true Integer keys - + final Map intKeyMap = Collections.singletonMap( Integer.valueOf(42), "answer");