NonStandardAposQuotedNamesTest.java

package com.fasterxml.jackson.core.read;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JUnit5TestBase;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.json.JsonReadFeature;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

class NonStandardAposQuotedNamesTest
        extends JUnit5TestBase
{
    private final JsonFactory STD_F = sharedStreamFactory();

    private final JsonFactory APOS_F = JsonFactory.builder()
            .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES)
            .build();

    @Test
    void singleQuotesDefault() throws Exception
    {
        _testSingleQuotesDefault(MODE_INPUT_STREAM);
        _testSingleQuotesDefault(MODE_INPUT_STREAM_THROTTLED);
        _testSingleQuotesDefault(MODE_DATA_INPUT);
        _testSingleQuotesDefault(MODE_READER);
    }

    @Test
    void singleQuotesEnabled() throws Exception
    {
        _testSingleQuotesEnabled(MODE_INPUT_STREAM);
        _testSingleQuotesEnabled(MODE_INPUT_STREAM_THROTTLED);
        _testSingleQuotesEnabled(MODE_DATA_INPUT);
        _testSingleQuotesEnabled(MODE_READER);
    }

    @Test
    void singleQuotesEscaped() throws Exception
    {
        _testSingleQuotesEscaped(MODE_INPUT_STREAM);
        _testSingleQuotesEscaped(MODE_INPUT_STREAM_THROTTLED);
        _testSingleQuotesEscaped(MODE_DATA_INPUT);
        _testSingleQuotesEscaped(MODE_READER);
    }

    /*
    /****************************************************************
    /* Secondary test methods
    /****************************************************************
     */

    /**
     * Test to verify that the default parser settings do not
     * accept single-quotes for String values (field names,
     * textual values)
     */
    private void _testSingleQuotesDefault(int mode) throws Exception
    {
        // First, let's see that by default they are not allowed
        String JSON = "[ 'text' ]";
        JsonParser p = createParser(STD_F, mode, JSON);
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        try {
            p.nextToken();
            fail("Expected exception");
        } catch (JsonParseException e) {
            verifyException(e, "Unexpected character ('''");
        } finally {
            p.close();
        }

        JSON = "{ 'a':1 }";
        p = createParser(STD_F, mode, JSON);
        assertToken(JsonToken.START_OBJECT, p.nextToken());
        try {
            p.nextToken();
            fail("Expected exception");
        } catch (JsonParseException e) {
            verifyException(e, "Unexpected character ('''");
        } finally {
            p.close();
        }
    }

    /**
     * Test to verify optional handling of single quotes,
     * to allow handling invalid (but, alas, common) JSON.
     */
    private void _testSingleQuotesEnabled(int mode) throws Exception
    {
        String JSON = "{ 'a' : 1, \"foobar\": 'b', '_abc\\u00A0e\\'23\\'':'d\\'foo\\'', '\"' : '\"\"', '':'' }";
        JsonParser p = createParser(APOS_F, mode, JSON);

        assertToken(JsonToken.START_OBJECT, p.nextToken());

        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("a", p.getText());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals("1", p.getText());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("foobar", p.getText());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        assertEquals("b", p.getText());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("_abc\u00A0e'23'", p.getText());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        assertEquals("d'foo'", p.getText());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("\"", p.getText());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        //assertEquals("\"\"", p.getText());

        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("", p.getText());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        assertEquals("", p.getText());

        assertToken(JsonToken.END_OBJECT, p.nextToken());
        p.close();


        JSON = "{'b':1,'array':[{'b':3}],'ob':{'b':4,'x':0,'y':3,'a':false }}";
        p = createParser(APOS_F, mode, JSON);
        assertToken(JsonToken.START_OBJECT, p.nextToken());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("b", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.START_OBJECT, p.nextToken());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("b", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(3, p.getIntValue());
        assertToken(JsonToken.END_OBJECT, p.nextToken());
        assertToken(JsonToken.END_ARRAY, p.nextToken());

        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertToken(JsonToken.START_OBJECT, p.nextToken());

        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("b", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(4, p.getIntValue());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("x", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(0, p.getIntValue());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("y", p.currentName());
        assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
        assertEquals(3, p.getIntValue());
        assertToken(JsonToken.FIELD_NAME, p.nextToken());
        assertEquals("a", p.currentName());
        assertToken(JsonToken.VALUE_FALSE, p.nextToken());

        assertToken(JsonToken.END_OBJECT, p.nextToken());
        assertToken(JsonToken.END_OBJECT, p.nextToken());
        p.close();
    }

    // test to verify that we implicitly allow escaping of apostrophe
    private void _testSingleQuotesEscaped(int mode) throws Exception
    {
        String JSON = "[ '16\\'' ]";
        JsonParser p = createParser(APOS_F, mode, JSON);

        assertToken(JsonToken.START_ARRAY, p.nextToken());
        assertToken(JsonToken.VALUE_STRING, p.nextToken());
        assertEquals("16'", p.getText());
        assertToken(JsonToken.END_ARRAY, p.nextToken());
        p.close();
    }

    // [core#721]: specific issue with enclosed unescaped double quotes
    @Test
    void singleQuotedKeys721() 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
    @Test
    void singleQuotedValues721() 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();
    }
}