diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 0273be4bae..dedd2b4f74 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -14,6 +14,11 @@ JSON library. === Releases === ------------------------------------------------------------------------ +2.13.1 (not yet released) + +#713: Incorrect parsing of single-quoted surrounded String values containing double quotes + (reported by wcarmon@github) + 2.13.0 (30-Sep-2021) #652: Misleading exception for input source when processing byte buffer diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java index 5d0d9b9b34..da788986c6 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java @@ -2022,10 +2022,9 @@ protected JsonToken _handleApos() throws IOException int i = (int) c; if (i <= '\\') { if (i == '\\') { - /* Although chars outside of BMP are to be escaped as - * an UTF-16 surrogate pair, does that affect decoding? - * For now let's assume it does not. - */ + // Although chars outside of BMP are to be escaped as + // an UTF-16 surrogate pair, does that affect decoding? + // For now let's assume it does not. c = _decodeEscaped(); } else if (i <= '\'') { if (i == '\'') { diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java index 58c1bebb6a..3d2fd99777 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java @@ -2152,7 +2152,10 @@ protected JsonToken _handleApos() throws IOException if (c == '\'') { break main_loop; } - if (codes[c] != 0) { + if ((codes[c] != 0) + // 13-Oct-2021, tatu: [core#721] Alas, regular quote is included as + // special, need to ignore here + && (c != INT_QUOTE)) { break ascii_loop; } outBuf[outPtr++] = (char) c; diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java index 9f30ce4f43..5ad5502d63 100644 --- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java @@ -2180,7 +2180,7 @@ protected String _parseAposName() throws IOException break; } // additional check to skip handling of double-quotes - if ((codes[ch] != 0) && (ch != '"')) { + if ((codes[ch] != 0) && (ch != INT_QUOTE)) { if (ch != '\\') { // Unquoted white space? // As per [JACKSON-208], call can now return: @@ -2770,18 +2770,19 @@ protected JsonToken _handleApos() throws IOException } while (_inputPtr < max) { c = (int) inputBuffer[_inputPtr++] & 0xFF; - if (c == INT_APOS || codes[c] != 0) { + if (c == INT_APOS) { + break main_loop; + } + if ((codes[c] != 0) + // 13-Oct-2021, tatu: [core#721] Alas, regular quote is included as + // special, need to ignore here + && (c != INT_QUOTE)) { break ascii_loop; } outBuf[outPtr++] = (char) c; } } - // Ok: end marker, escape or multi-byte? - if (c == INT_APOS) { - break main_loop; - } - switch (codes[c]) { case 1: // backslash c = _decodeEscaped(); diff --git a/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java b/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java index 89613a9195..619cd90f57 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/NonStandardAposQuotedNamesTest.java @@ -167,4 +167,62 @@ private void _testSingleQuotesEscaped(int mode) throws Exception assertToken(JsonToken.END_ARRAY, p.nextToken()); p.close(); } + + // [core#721]: specific issue with enclosed unescaped double quotes + public void testSingleQuotedKeys721() throws Exception + { + // empty + _testSingleQuotedKeys721("{ '\"\"': 'value'}", "\"\""); + // non-empty + _testSingleQuotedKeys721("{ '\"key\"': 'value'}", "\"key\""); + } + + private void _testSingleQuotedKeys721(String doc, String expKey) throws Exception + { + _testSingleQuotedKeys721(MODE_READER, doc, expKey); + _testSingleQuotedKeys721(MODE_INPUT_STREAM, doc, expKey); + _testSingleQuotedKeys721(MODE_INPUT_STREAM_THROTTLED, doc, expKey); + _testSingleQuotedKeys721(MODE_DATA_INPUT, doc, expKey); + } + + private void _testSingleQuotedKeys721(int mode, String doc, String expKey) throws Exception + { + JsonParser p = createParser(APOS_F, mode, doc); + + assertToken(JsonToken.START_OBJECT, p.nextToken()); + assertEquals(expKey, p.nextFieldName()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals("value", p.getText()); + assertToken(JsonToken.END_OBJECT, p.nextToken()); + p.close(); + } + + // [core#721]: specific issue with enclosed unescaped double quotes + public void testSingleQuotedValues721() throws Exception + { + // empty + _testSingleQuotedValues721("{ \"bar\": '\"\"'}", "\"\""); + // non-empty + _testSingleQuotedValues721("{ \"bar\": '\"stuff\"'}", "\"stuff\""); + } + + private void _testSingleQuotedValues721(String doc, String expValue) throws Exception + { + _testSingleQuotedValues721(MODE_READER, doc, expValue); + _testSingleQuotedValues721(MODE_INPUT_STREAM, doc, expValue); + _testSingleQuotedValues721(MODE_INPUT_STREAM_THROTTLED, doc, expValue); + _testSingleQuotedValues721(MODE_DATA_INPUT, doc, expValue); + } + + private void _testSingleQuotedValues721(int mode, String doc, String expValue) throws Exception + { + JsonParser p = createParser(APOS_F, mode, doc); + + assertToken(JsonToken.START_OBJECT, p.nextToken()); + assertEquals("bar", p.nextFieldName()); + assertToken(JsonToken.VALUE_STRING, p.nextToken()); + assertEquals(expValue, p.getText()); + assertToken(JsonToken.END_OBJECT, p.nextToken()); + p.close(); + } }