Skip to content

Commit

Permalink
Fixed #116
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Jun 1, 2019
1 parent 10f2dc1 commit c427465
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 42 deletions.
4 changes: 4 additions & 0 deletions release-notes/CREDITS-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ Andrey Somov (asomov@github)
Alon Bar-Lev (alonbl@github)
* Contributed #100: (properties) Add an option to specify properties prefix
(2.10.0)

Stehan Leh (stefanleh@github)
* Reported #116: (yaml) Error handling "null" String when Feature.MINIMIZE_QUOTES is active
(2.10.0)
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Modules:
(contributed by Andrey S)
#108: (yaml) Add new `CsvParser.Feature.ALLOW_COMMENTS` to replace deprecated
`JsonParser.Feature.ALLOW_YAML_COMMENTS`
#116: Error handling "null" String when Feature.MINIMIZE_QUOTES is active
(reported by Stefan L)
- Add JDK9+ module info using Moditect plugin

2.9.9 (16-May-2019)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,23 @@ private Feature(boolean defaultState) {
*/
// 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<String> RESERVED_NAMES = new HashSet<>(Arrays.asList(
private final static Set<String> MUST_QUOTE_NAMES = 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"
));

/**
* As per YAML <a href="https://yaml.org/type/null.html">null</a>
* and <a href="https://yaml.org/type/bool.html">boolean</a> type specs,
* better retain quoting for some values
*/
private final static Set<String> MUST_QUOTE_VALUES = new HashSet<>(Arrays.asList(
"true", "True", "TRUE", "false", "False", "FALSE",
"null", "Null", "NULL"
));

/*
/**********************************************************
/* Configuration
Expand Down Expand Up @@ -444,35 +454,9 @@ public final void writeStringField(String fieldName, String value)
private final void _writeFieldName(String name) throws IOException
{
_writeScalar(name, "string",
_needQuoting(name) ? STYLE_QUOTED : STYLE_UNQUOTED_NAME);
_nameNeedsQuoting(name) ? STYLE_QUOTED : STYLE_UNQUOTED_NAME);
}

private boolean _needQuoting(String name) {
if (name.length() == 0) {
return false;
}
switch (name.charAt(0)) {
// First, reserved name starting chars:
case 'f': // false
case 'o': // on/off
case 'n': // no
case 't': // true
case 'y': // yes
case 'F': // False
case 'O': // On/Off
case 'N': // No
case 'T': // True
case 'Y': // Yes
return RESERVED_NAMES.contains(name);

// And then numbers
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '-' : case '+': case '.':
return true;
}
return false;
}
/*
/**********************************************************
/* Public API: low-level I/O
Expand Down Expand Up @@ -588,25 +572,25 @@ public void writeString(String text) throws IOException,JsonGenerationException
return;
}
_verifyValueWrite("write String value");
DumperOptions.ScalarStyle style;

// [dataformats-text#50]: Empty String always quoted
if (text.isEmpty()) {
style = STYLE_QUOTED;
} else if (Feature.MINIMIZE_QUOTES.enabledIn(_formatFeatures)) {
if (isBooleanContent(text)) {
style = STYLE_QUOTED;
// If this string could be interpreted as a number, it must be quoted.
} else if (Feature.ALWAYS_QUOTE_NUMBERS_AS_STRINGS.enabledIn(_formatFeatures)
&& PLAIN_NUMBER_P.matcher(text).matches()) {
_writeScalar(text, "string", STYLE_QUOTED);
return;
}
DumperOptions.ScalarStyle style;
if (Feature.MINIMIZE_QUOTES.enabledIn(_formatFeatures)) {
// If one of reserved values ("true", "null"), or, number, preserve quoting:
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;
} else {
style = STYLE_PLAIN;
}
_writeScalar(text, "string", style);
return;
} else {
if (Feature.LITERAL_BLOCK_STYLE.enabledIn(_formatFeatures) && text.indexOf('\n') >= 0) {
style = STYLE_LITERAL;
Expand All @@ -617,10 +601,6 @@ public void writeString(String text) throws IOException,JsonGenerationException
_writeScalar(text, "string", style);
}

private boolean isBooleanContent(String text) {
return "true".equals(text) || "false".equals(text);
}

@Override
public void writeString(char[] text, int offset, int len) throws IOException
{
Expand Down Expand Up @@ -944,6 +924,47 @@ private String _base64encode(final Base64Variant b64v, final byte[] input, final
return sb.toString();
}

private boolean _nameNeedsQuoting(String name) {
if (name.length() == 0) { // empty String does indeed require quoting
return true;
}
switch (name.charAt(0)) {
// First, reserved name starting chars:
case 'f': // false
case 'o': // on/off
case 'n': // no
case 't': // true
case 'y': // yes
case 'F': // False
case 'O': // On/Off
case 'N': // No
case 'T': // True
case 'Y': // Yes
return MUST_QUOTE_NAMES.contains(name);

// And then numbers
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '-' : case '+': case '.':
return true;
}
return false;
}

private boolean _valueNeedsQuoting(String name) {
switch (name.charAt(0)) { // caller ensures no empty String
// First, reserved name starting chars:
case 'f': // false
case 'n': // null
case 't': // true
case 'F': // False/FALSE
case 'N': // Null/NULL
case 'T': // True/TRUE
return MUST_QUOTE_VALUES.contains(name);
}
return false;
}

protected String _lf() {
return _outputOptions.getLineBreak().getString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,34 @@ public void testMinimizeQuotesWithBooleanContent() throws Exception
"key: true", yaml);
}

public void testMinimizeQuotesWithNulls() throws Exception
{
Map<String, Object> content = new HashMap<String, Object>();
content.put("key", "null");
String yaml = MINIM_MAPPER.writeValueAsString(content).trim();
assertEquals("---\n" +
"key: \"null\"", yaml);

content.clear();
content.put("key", "Null");
yaml = MINIM_MAPPER.writeValueAsString(content).trim();
assertEquals("---\n" +
"key: \"Null\"", yaml);

content.clear();
content.put("key", "NULL");
yaml = MINIM_MAPPER.writeValueAsString(content).trim();
assertEquals("---\n" +
"key: \"NULL\"", yaml);

// but not for any casing
content.clear();
content.put("key", "nuLL");
yaml = MINIM_MAPPER.writeValueAsString(content).trim();
assertEquals("---\n" +
"key: nuLL", yaml);
}

public void testLiteralStringsMultiLine() throws Exception
{
Map<String, Object> content = new HashMap<String, Object>();
Expand Down

0 comments on commit c427465

Please sign in to comment.