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 120b2aaf9..3bba7f344 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 @@ -184,6 +184,19 @@ private Feature(boolean defaultState) { "on", "On", "ON", "off", "Off", "OFF" )); + /** + * As per YAML null + * and boolean type specs, + * better retain quoting for some values + */ + private final static Set RESERVED_VALUES = new HashSet<>(Arrays.asList( + "y", "Y", "n", "N", + "yes", "Yes", "YES", "no", "No", "NO", + "true", "True", "TRUE", "false", "False", "FALSE", + "on", "On", "ON", "off", "Off", "OFF", + "null", "Null", "NULL" +)); + /* /********************************************************** /* Configuration @@ -571,10 +584,12 @@ public void writeString(String text) throws IOException,JsonGenerationException } _verifyValueWrite("write String value"); Character style = STYLE_QUOTED; - if (Feature.MINIMIZE_QUOTES.enabledIn(_formatFeatures) && !isBooleanContent(text)) { + if (Feature.MINIMIZE_QUOTES.enabledIn(_formatFeatures)) { // If this string could be interpreted as a number, it must be quoted. - if (Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS.enabledIn(_formatFeatures) - && PLAIN_NUMBER_P.matcher(text).matches()) { + if (_valueNeedsQuoting(text) + || (Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS.enabledIn(_formatFeatures) + && PLAIN_NUMBER_P.matcher(text).matches()) + ) { style = STYLE_QUOTED; } else if (text.indexOf('\n') >= 0) { style = STYLE_LITERAL; @@ -587,8 +602,22 @@ public void writeString(String text) throws IOException,JsonGenerationException _writeScalar(text, "string", style); } - private boolean isBooleanContent(String text) { - return text.equals("true") || text.equals("false"); + private boolean _valueNeedsQuoting(String name) { + switch (name.charAt(0)) { // caller ensures no empty String + // First, reserved name starting chars: + case 'f': // false + case 'o': // on/off + case 'n': // null/n/no + case 't': // true + case 'y': // y/yes + case 'F': // False/FALSE + case 'O': // On/Off/ON/OFF + case 'N': // Null/NULL/N/No/NO + case 'T': // True/TRUE + case 'Y': // Y/Yes/YES + return RESERVED_VALUES.contains(name); + } + return false; } @Override diff --git a/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/misc/ReservedValuesTest.java b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/misc/ReservedValuesTest.java new file mode 100644 index 000000000..7edd71064 --- /dev/null +++ b/yaml/src/test/java/com/fasterxml/jackson/dataformat/yaml/misc/ReservedValuesTest.java @@ -0,0 +1,35 @@ +package com.fasterxml.jackson.dataformat.yaml.misc; + +import java.util.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.ModuleTestBase; + +public class ReservedValuesTest extends ModuleTestBase +{ + private final ObjectMapper MAPPER = newObjectMapper(); + + public void testQuotingOfBooleanValues() throws Exception + { + for (String value : new String[] { + "null", "Null", "NULL", + "true", "True", "TRUE", + "false", "False", "FALSE", + "yes", "Yes", "YES", + "no" "No", "NO", + "y", "Y", "n", "N", + "on", "On", "ON", + "off" "Off", "OFF" + }) { + _testQuotingOfBooleanValues(value); + } + } + + private void _testQuotingOfBooleanValues(String value) throws Exception + { + final Map input = Collections.singletonMap("key", value); + final String doc = trimDocMarker(MAPPER.writeValueAsString(input).trim()); + + assertEquals("key: \""+value+"\"", doc); + } +}