Skip to content

Commit c2ca290

Browse files
authored
Leading plus sign (non-standard support - optional) (#774)
1 parent a0620ad commit c2ca290

8 files changed

+187
-58
lines changed

src/main/java/com/fasterxml/jackson/core/JsonParser.java

+5
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ public enum Feature {
182182
@Deprecated
183183
ALLOW_NUMERIC_LEADING_ZEROS(false),
184184

185+
/**
186+
* @deprecated Use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS} instead
187+
*/
188+
ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS(false),
189+
185190
/**
186191
* @deprecated Use {@link com.fasterxml.jackson.core.json.JsonReadFeature#ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS} instead
187192
*/

src/main/java/com/fasterxml/jackson/core/json/JsonReadFeature.java

+15
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ public enum JsonReadFeature
107107
@SuppressWarnings("deprecation")
108108
ALLOW_LEADING_ZEROS_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS),
109109

110+
/**
111+
* Feature that determines whether parser will allow
112+
* JSON decimal numbers to start with a plus sign
113+
* (like: +123). If enabled, no exception is thrown, and the number
114+
* is parsed as though a leading sign had not been present.
115+
*<p>
116+
* Since JSON specification does not allow leading plus signs,
117+
* this is a non-standard feature, and as such disabled by default.
118+
*
119+
* @since 2.14
120+
*/
121+
@SuppressWarnings("deprecation")
122+
ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS(false, JsonParser.Feature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS),
123+
124+
110125
/**
111126
* Feature that determines whether parser will allow
112127
* JSON decimal numbers to start with a decimal point

src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java

+27-11
Original file line numberDiff line numberDiff line change
@@ -777,12 +777,15 @@ public final JsonToken nextToken() throws IOException
777777
break;
778778

779779
case '-':
780-
/* Should we have separate handling for plus? Although
781-
* it is not allowed per se, it may be erroneously used,
782-
* and could be indicate by a more specific error message.
783-
*/
784780
t = _parseNegNumber();
785781
break;
782+
case '+':
783+
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) {
784+
t = _handleOddValue(i);
785+
} else {
786+
t = _parsePosNumber();
787+
}
788+
break;
786789
case '.': // [core#61]]
787790
t = _parseFloatThatStartsWithPeriod();
788791
break;
@@ -1453,32 +1456,45 @@ private final JsonToken _parseFloat(int ch, int startPtr, int ptr, boolean neg,
14531456
return resetFloat(neg, intLen, fractLen, expLen);
14541457
}
14551458

1459+
protected final JsonToken _parsePosNumber() throws IOException
1460+
{
1461+
return _parsePossibleNumber(false);
1462+
}
1463+
14561464
protected final JsonToken _parseNegNumber() throws IOException
1465+
{
1466+
return _parsePossibleNumber(true);
1467+
}
1468+
1469+
private JsonToken _parsePossibleNumber(final boolean negative) throws IOException
14571470
{
14581471
int ptr = _inputPtr;
1459-
int startPtr = ptr-1; // to include sign/digit already read
1472+
int startPtr = negative ? ptr-1 : ptr; // to include sign/digit already read
14601473
final int inputLen = _inputEnd;
14611474

14621475
if (ptr >= inputLen) {
1463-
return _parseNumber2(true, startPtr);
1476+
return _parseNumber2(negative, startPtr);
14641477
}
14651478
int ch = _inputBuffer[ptr++];
14661479
// First check: must have a digit to follow minus sign
14671480
if (ch > INT_9 || ch < INT_0) {
14681481
_inputPtr = ptr;
1469-
return _handleInvalidNumberStart(ch, true);
1482+
if (ch == INT_PERIOD) {
1483+
return _parseFloatThatStartsWithPeriod();
1484+
}
1485+
return _handleInvalidNumberStart(ch, negative);
14701486
}
14711487
// One special case, leading zero(es):
14721488
if (ch == INT_0) {
1473-
return _parseNumber2(true, startPtr);
1489+
return _parseNumber2(negative, startPtr);
14741490
}
14751491
int intLen = 1; // already got one
14761492

14771493
// First let's get the obligatory integer part:
14781494
int_loop:
14791495
while (true) {
14801496
if (ptr >= inputLen) {
1481-
return _parseNumber2(true, startPtr);
1497+
return _parseNumber2(negative, startPtr);
14821498
}
14831499
ch = (int) _inputBuffer[ptr++];
14841500
if (ch < INT_0 || ch > INT_9) {
@@ -1489,7 +1505,7 @@ protected final JsonToken _parseNegNumber() throws IOException
14891505

14901506
if (ch == INT_PERIOD || ch == INT_e || ch == INT_E) {
14911507
_inputPtr = ptr;
1492-
return _parseFloat(ch, startPtr, ptr, true, intLen);
1508+
return _parseFloat(ch, startPtr, ptr, negative, intLen);
14931509
}
14941510
--ptr;
14951511
_inputPtr = ptr;
@@ -1498,7 +1514,7 @@ protected final JsonToken _parseNegNumber() throws IOException
14981514
}
14991515
int len = ptr-startPtr;
15001516
_textBuffer.resetWithShared(_inputBuffer, startPtr, len);
1501-
return resetInt(true, intLen);
1517+
return resetInt(negative, intLen);
15021518
}
15031519

15041520
/**

src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java

+33-14
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,13 @@ public JsonToken nextToken() throws IOException
660660
case '-':
661661
t = _parseNegNumber();
662662
break;
663-
664-
// Should we have separate handling for plus? Although
665-
// it is not allowed per se, it may be erroneously used,
666-
// and could be indicate by a more specific error message.
663+
case '+':
664+
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) {
665+
t = _handleUnexpectedValue(i);
666+
} else {
667+
t = _parsePosNumber();
668+
}
669+
break;
667670
case '.': // as per [core#611]
668671
t = _parseFloatThatStartsWithPeriod();
669672
break;
@@ -729,9 +732,11 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException
729732
return (_currToken = JsonToken.VALUE_NULL);
730733
case '-':
731734
return (_currToken = _parseNegNumber());
732-
// Should we have separate handling for plus? Although it is not allowed
733-
// per se, it may be erroneously used, and could be indicated by a more
734-
// specific error message.
735+
case '+':
736+
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) {
737+
return (_currToken = _handleUnexpectedValue(i));
738+
}
739+
return (_currToken = _parsePosNumber());
735740
case '.': // as per [core#611]
736741
return (_currToken = _parseFloatThatStartsWithPeriod());
737742
case '0':
@@ -1067,27 +1072,41 @@ protected JsonToken _parsePosNumber(int c) throws IOException
10671072
// And there we have it!
10681073
return resetInt(false, intLen);
10691074
}
1070-
1075+
1076+
protected JsonToken _parsePosNumber() throws IOException
1077+
{
1078+
return _parsePossibleNumber(false);
1079+
}
1080+
10711081
protected JsonToken _parseNegNumber() throws IOException
1082+
{
1083+
return _parsePossibleNumber(true);
1084+
}
1085+
1086+
private JsonToken _parsePossibleNumber(boolean negative) throws IOException
10721087
{
10731088
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
10741089
int outPtr = 0;
10751090

1076-
// Need to prepend sign?
1077-
outBuf[outPtr++] = '-';
1091+
if (negative) {
1092+
// Need to prepend sign?
1093+
outBuf[outPtr++] = '-';
1094+
}
10781095
int c = _inputData.readUnsignedByte();
10791096
outBuf[outPtr++] = (char) c;
10801097
// Note: must be followed by a digit
10811098
if (c <= INT_0) {
10821099
// One special case: if first char is 0 need to check no leading zeroes
10831100
if (c == INT_0) {
10841101
c = _handleLeadingZeroes();
1102+
} else if (c == INT_PERIOD) {
1103+
return _parseFloatThatStartsWithPeriod();
10851104
} else {
1086-
return _handleInvalidNumberStart(c, true);
1105+
return _handleInvalidNumberStart(c, negative);
10871106
}
10881107
} else {
10891108
if (c > INT_9) {
1090-
return _handleInvalidNumberStart(c, true);
1109+
return _handleInvalidNumberStart(c, negative);
10911110
}
10921111
c = _inputData.readUnsignedByte();
10931112
}
@@ -1101,7 +1120,7 @@ protected JsonToken _parseNegNumber() throws IOException
11011120
c = _inputData.readUnsignedByte();
11021121
}
11031122
if (c == '.' || c == 'e' || c == 'E') {
1104-
return _parseFloat(outBuf, outPtr, c, true, intLen);
1123+
return _parseFloat(outBuf, outPtr, c, negative, intLen);
11051124
}
11061125
_textBuffer.setCurrentLength(outPtr);
11071126
// As per [core#105], need separating space between root values; check here
@@ -1110,7 +1129,7 @@ protected JsonToken _parseNegNumber() throws IOException
11101129
_verifyRootSpace();
11111130
}
11121131
// And there we have it!
1113-
return resetInt(true, intLen);
1132+
return resetInt(negative, intLen);
11141133
}
11151134

11161135
/**

src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java

+37-16
Original file line numberDiff line numberDiff line change
@@ -814,9 +814,13 @@ public JsonToken nextToken() throws IOException
814814
case '-':
815815
t = _parseNegNumber();
816816
break;
817-
818-
// Should we have separate handling for plus? Although it is not allowed per se,
819-
// it may be erroneously used, and could be indicate by a more specific error message.
817+
case '+':
818+
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) {
819+
t = _handleUnexpectedValue(i);
820+
} else {
821+
t = _parsePosNumber();
822+
}
823+
break;
820824
case '.': // [core#611]:
821825
t = _parseFloatThatStartsWithPeriod();
822826
break;
@@ -882,9 +886,11 @@ private final JsonToken _nextTokenNotInObject(int i) throws IOException
882886
return (_currToken = JsonToken.VALUE_NULL);
883887
case '-':
884888
return (_currToken = _parseNegNumber());
885-
886-
// Should we have separate handling for plus? Although it is not allowed per se,
887-
// it may be erroneously used, and could be indicate by a more specific error message.
889+
case '+':
890+
if (!isEnabled(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS.mappedFeature())) {
891+
return (_currToken = _handleUnexpectedValue(i));
892+
}
893+
return (_currToken = _parsePosNumber());
888894
case '.': // [core#611]:
889895
return (_currToken = _parseFloatThatStartsWithPeriod());
890896
case '0':
@@ -1475,14 +1481,26 @@ protected JsonToken _parsePosNumber(int c) throws IOException
14751481
// And there we have it!
14761482
return resetInt(false, intLen);
14771483
}
1478-
1484+
1485+
protected JsonToken _parsePosNumber() throws IOException
1486+
{
1487+
return _parsePossibleNumber(false);
1488+
}
1489+
14791490
protected JsonToken _parseNegNumber() throws IOException
1491+
{
1492+
return _parsePossibleNumber(true);
1493+
}
1494+
1495+
private JsonToken _parsePossibleNumber(boolean negative) throws IOException
14801496
{
14811497
char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
14821498
int outPtr = 0;
14831499

1484-
// Need to prepend sign?
1485-
outBuf[outPtr++] = '-';
1500+
if (negative) {
1501+
// Need to prepend sign?
1502+
outBuf[outPtr++] = '-';
1503+
}
14861504
// Must have something after sign too
14871505
if (_inputPtr >= _inputEnd) {
14881506
_loadMoreGuaranteed();
@@ -1492,13 +1510,16 @@ protected JsonToken _parseNegNumber() throws IOException
14921510
if (c <= INT_0) {
14931511
// One special case: if first char is 0, must not be followed by a digit
14941512
if (c != INT_0) {
1495-
return _handleInvalidNumberStart(c, true);
1513+
if (c == INT_PERIOD) {
1514+
return _parseFloatThatStartsWithPeriod();
1515+
}
1516+
return _handleInvalidNumberStart(c, negative);
14961517
}
14971518
c = _verifyNoLeadingZeroes();
14981519
} else if (c > INT_9) {
1499-
return _handleInvalidNumberStart(c, true);
1520+
return _handleInvalidNumberStart(c, negative);
15001521
}
1501-
1522+
15021523
// Ok: we can first just add digit we saw first:
15031524
outBuf[outPtr++] = (char) c;
15041525
int intLen = 1;
@@ -1510,7 +1531,7 @@ protected JsonToken _parseNegNumber() throws IOException
15101531
while (true) {
15111532
if (_inputPtr >= end) {
15121533
// Long enough to be split across boundary, so:
1513-
return _parseNumber2(outBuf, outPtr, true, intLen);
1534+
return _parseNumber2(outBuf, outPtr, negative, intLen);
15141535
}
15151536
c = (int) _inputBuffer[_inputPtr++] & 0xFF;
15161537
if (c < INT_0 || c > INT_9) {
@@ -1520,9 +1541,9 @@ protected JsonToken _parseNegNumber() throws IOException
15201541
outBuf[outPtr++] = (char) c;
15211542
}
15221543
if (c == INT_PERIOD || c == INT_e || c == INT_E) {
1523-
return _parseFloat(outBuf, outPtr, c, true, intLen);
1544+
return _parseFloat(outBuf, outPtr, c, negative, intLen);
15241545
}
1525-
1546+
15261547
--_inputPtr; // to push back trailing char (comma etc)
15271548
_textBuffer.setCurrentLength(outPtr);
15281549
// As per #105, need separating space between root values; check here
@@ -1531,7 +1552,7 @@ protected JsonToken _parseNegNumber() throws IOException
15311552
}
15321553

15331554
// And there we have it!
1534-
return resetInt(true, intLen);
1555+
return resetInt(negative, intLen);
15351556
}
15361557

15371558
// Method called to handle parsing when input is split across buffer boundary

src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class FastParserNonStandardNumberParsingTest
99
{
1010
private final JsonFactory fastFactory =
1111
JsonFactory.builder()
12+
.enable(JsonReadFeature.ALLOW_LEADING_PLUS_SIGN_FOR_NUMBERS)
1213
.enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS)
1314
.enable(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS)
1415
.enable(StreamReadFeature.USE_FAST_DOUBLE_PARSER)

0 commit comments

Comments
 (0)