NonStandardParserFeaturesTest.java
package com.fasterxml.jackson.core.read;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import static org.junit.jupiter.api.Assertions.*;
class NonStandardParserFeaturesTest
extends JUnit5TestBase
{
private final JsonFactory STD_F = sharedStreamFactory();
private final JsonFactory LEADING_ZERO_F = JsonFactory.builder()
.enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS)
.build();
@SuppressWarnings("deprecation")
@Test
void defaults() {
assertFalse(STD_F.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));
assertFalse(STD_F.isEnabled(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS));
}
@Test
void nonStandardAnyCharQuoting() throws Exception
{
_testNonStandardBackslashQuoting(MODE_INPUT_STREAM);
_testNonStandardBackslashQuoting(MODE_INPUT_STREAM_THROTTLED);
_testNonStandardBackslashQuoting(MODE_DATA_INPUT);
_testNonStandardBackslashQuoting(MODE_READER);
_testNonStandardBackslashQuoting(MODE_READER_THROTTLED);
}
@Test
void leadingZeroesUTF8() throws Exception {
_testLeadingZeroes(MODE_INPUT_STREAM, false);
_testLeadingZeroes(MODE_INPUT_STREAM, true);
_testLeadingZeroes(MODE_INPUT_STREAM_THROTTLED, false);
_testLeadingZeroes(MODE_INPUT_STREAM_THROTTLED, true);
// 17-May-2016, tatu: With DataInput, must have trailing space
// since there's no way to detect end of input
_testLeadingZeroes(MODE_DATA_INPUT, true);
}
@Test
void leadingZeroesReader() throws Exception {
_testLeadingZeroes(MODE_READER, false);
_testLeadingZeroes(MODE_READER, true);
_testLeadingZeroes(MODE_READER_THROTTLED, false);
_testLeadingZeroes(MODE_READER_THROTTLED, true);
}
// allow NaN
@Test
void allowNaN() throws Exception {
_testAllowNaN(MODE_INPUT_STREAM);
_testAllowNaN(MODE_INPUT_STREAM_THROTTLED);
_testAllowNaN(MODE_DATA_INPUT);
_testAllowNaN(MODE_READER);
_testAllowNaN(MODE_READER_THROTTLED);
}
// allow +Inf/-Inf
@Test
void allowInfinity() throws Exception {
_testAllowInf(MODE_INPUT_STREAM);
_testAllowInf(MODE_INPUT_STREAM_THROTTLED);
_testAllowInf(MODE_DATA_INPUT);
_testAllowInf(MODE_READER);
_testAllowInf(MODE_READER_THROTTLED);
}
/*
/****************************************************************
/* Secondary test methods
/****************************************************************
*/
private void _testNonStandardBackslashQuoting(int mode) throws Exception
{
// first: verify that we get an exception
final String JSON = q("\\'");
JsonParser p = createParser(STD_F, mode, JSON);
try {
p.nextToken();
p.getText();
fail("Should have thrown an exception for doc <"+JSON+">");
} catch (JsonParseException e) {
verifyException(e, "unrecognized character escape");
} finally {
p.close();
}
// and then verify it's ok...
JsonFactory f = JsonFactory.builder()
.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true)
.build();
p = createParser(f, mode, JSON);
assertToken(JsonToken.VALUE_STRING, p.nextToken());
assertEquals("'", p.getText());
p.close();
}
private void _testLeadingZeroes(int mode, boolean appendSpace) throws Exception
{
// first: verify that we get an exception
String JSON = "00003";
if (appendSpace) {
JSON += " ";
}
JsonParser p = createParser(STD_F, mode, JSON);
try {
p.nextToken();
p.getText();
fail("Should have thrown an exception for doc <"+JSON+">");
} catch (JsonParseException e) {
verifyException(e, "invalid numeric value");
}
p.close();
// but not just as root value
p = createParser(STD_F, mode, "[ -000 ]");
assertToken(JsonToken.START_ARRAY, p.nextToken());
try {
p.nextToken();
fail("Should have thrown an exception for doc <"+JSON+">");
} catch (JsonParseException e) {
verifyException(e, "invalid numeric value");
}
p.close();
// and then verify it's ok when enabled
p = createParser(LEADING_ZERO_F, mode, JSON);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(3, p.getIntValue());
assertEquals("3", p.getText());
p.close();
// Plus, also: verify that leading zero magnitude is ok:
JSON = "0"+Integer.MAX_VALUE;
if (appendSpace) {
JSON += " ";
}
p = createParser(LEADING_ZERO_F, mode, JSON);
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(String.valueOf(Integer.MAX_VALUE), p.getText());
assertEquals(Integer.MAX_VALUE, p.getIntValue());
Number nr = p.getNumberValue();
assertSame(Integer.class, nr.getClass());
p.close();
// and also check non-root "long zero"
p = createParser(LEADING_ZERO_F, mode, "[000]");
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_INT, p.nextToken());
assertEquals(0, p.getIntValue());
// 03-Jan-2020, tatu: Actually not 100% sure if we ought to retain invalid
// representation or not? Won't, for now:
assertEquals("0", p.getText());
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}
private void _testAllowNaN(int mode) throws Exception
{
final String JSON = "[ NaN]";
// without enabling, should get an exception
JsonParser p = createParser(STD_F, mode, JSON);
assertToken(JsonToken.START_ARRAY, p.nextToken());
try {
p.nextToken();
fail("Expected exception");
} catch (Exception e) {
verifyException(e, "non-standard");
} finally {
p.close();
}
// we can enable it dynamically (impl detail)
JsonFactory f = JsonFactory.builder()
.configure(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS, true)
.build();
p = createParser(f, mode, JSON);
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
double d = p.getDoubleValue();
assertTrue(Double.isNaN(d));
assertEquals("NaN", p.getText());
// [Issue#98]
try {
/*BigDecimal dec =*/ p.getDecimalValue();
fail("Should fail when trying to access NaN as BigDecimal");
} catch (NumberFormatException e) {
verifyException(e, "can not be deserialized as `java.math.BigDecimal`");
}
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
// finally, should also work with skipping
p = createParser(f, mode, JSON);
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}
private void _testAllowInf(int mode) throws Exception
{
final String JSON = "[ -INF, +INF, +Infinity, Infinity, -Infinity ]";
// without enabling, should get an exception
JsonParser p = createParser(STD_F, mode, JSON);
assertToken(JsonToken.START_ARRAY, p.nextToken());
try {
p.nextToken();
fail("Expected exception");
} catch (Exception e) {
verifyException(e, "Non-standard token '-INF'");
} finally {
p.close();
}
JsonFactory f = JsonFactory.builder()
.enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)
.build();
p = createParser(f, mode, JSON);
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
double d = p.getDoubleValue();
assertEquals("-INF", p.getText());
assertTrue(Double.isInfinite(d));
assertEquals(Double.NEGATIVE_INFINITY, d);
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
d = p.getDoubleValue();
assertEquals("+INF", p.getText());
assertTrue(Double.isInfinite(d));
assertEquals(Double.POSITIVE_INFINITY, d);
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
d = p.getDoubleValue();
assertEquals("+Infinity", p.getText());
assertTrue(Double.isInfinite(d));
assertEquals(Double.POSITIVE_INFINITY, d);
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
d = p.getDoubleValue();
assertEquals("Infinity", p.getText());
assertTrue(Double.isInfinite(d));
assertEquals(Double.POSITIVE_INFINITY, d);
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
d = p.getDoubleValue();
assertEquals("-Infinity", p.getText());
assertTrue(Double.isInfinite(d));
assertEquals(Double.NEGATIVE_INFINITY, d);
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
// should also work with skipping
p = createParser(f, mode, JSON);
assertToken(JsonToken.START_ARRAY, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
assertToken(JsonToken.END_ARRAY, p.nextToken());
p.close();
}
}