You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/09/03 23:16:03 UTC
(groovy) branch master updated: GROOVY-12329: JsonSlurper: bound the length of a JSON number token
This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new fab3f6b3cc GROOVY-12329: JsonSlurper: bound the length of a JSON number token
fab3f6b3cc is described below
commit fab3f6b3ccf0102499ddc0f576a6bb68578f43c5
Author: Paul King <pa...@asert.com.au>
AuthorDate: Wed Sep 2 16:51:45 2026 +1000
GROOVY-12329: JsonSlurper: bound the length of a JSON number token
A digit run past the long range is converted with BigInteger, and one
with a decimal point or exponent with BigDecimal. Both conversions are
superlinear in the digit count and neither token was bounded anywhere in
groovy-json, so a document of a few hundred KB cost seconds of CPU: a
400,000-digit number took about two seconds on every parser variant, and
doubling the digits quadrupled the time.
A number token longer than maxNumberLength is now rejected with a
JsonException before the conversion that would pay for it. The default
of 1000 characters matches Jackson's StreamReadConstraints, so JSON is
bounded consistently with the Jackson-backed YAML/TOML/CSV slurpers, and
follows the shape GROOVY-12064 established for nesting depth: a per
instance setter on JsonSlurper and JsonSlurperClassic, the
groovy.json.maxNumberLength system property to override globally, and a
value of 0 or less to disable the check. Rejecting a 400,000-digit
number now takes single-digit milliseconds instead of two seconds.
All four JsonParserType variants and the classic parser funnel through
their own conversion, so each enforces the bound where it reads the
token. The INDEX_OVERLAY and LAX parsers defer conversion to first
access, so they check while decoding rather than in NumberValue: a
document over the limit is rejected by the parse that read it, not by a
later read of the value.
This tightens what the parsers accept. A number longer than 1000
characters that parsed before is now rejected by default; callers that
legitimately carry such values can raise or disable the limit.
---
.../src/main/java/groovy/json/JsonSlurper.java | 33 +++++
.../main/java/groovy/json/JsonSlurperClassic.java | 61 ++++++++-
.../groovy/json/internal/BaseJsonParser.java | 46 +++++++
.../apache/groovy/json/internal/CharScanner.java | 37 ++++++
.../groovy/json/internal/JsonFastParser.java | 4 +
.../groovy/json/internal/JsonParserCharArray.java | 2 +-
.../apache/groovy/json/internal/JsonParserLax.java | 4 +
.../internal/JsonParserUsingCharacterSource.java | 1 +
.../json/JsonSlurperMaxNumberLengthTest.groovy | 146 +++++++++++++++++++++
9 files changed, 330 insertions(+), 4 deletions(-)
diff --git a/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurper.java b/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurper.java
index 17667730eb..3802cbf89b 100644
--- a/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurper.java
+++ b/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurper.java
@@ -102,6 +102,7 @@ public class JsonSlurper {
private boolean lazyChop = true;
private boolean checkDates = true;
private int maxNestingDepth = Integer.getInteger("groovy.json.maxNestingDepth", BaseJsonParser.DEFAULT_MAX_NESTING_DEPTH);
+ private int maxNumberLength = Integer.getInteger("groovy.json.maxNumberLength", BaseJsonParser.DEFAULT_MAX_NUMBER_LENGTH);
private JsonParserType type = JsonParserType.CHAR_BUFFER;
@@ -248,6 +249,36 @@ public class JsonSlurper {
return this;
}
+ /**
+ * The maximum length, in characters, of a single number token the parser will accept before
+ * throwing a {@link JsonException}. Converting a digit run past the {@code long} range builds
+ * a {@link java.math.BigInteger} or {@link java.math.BigDecimal}, and decimal conversion is
+ * superlinear in the digit count, so an uncapped token lets a small document cost quadratic
+ * CPU. A value of {@code 0} or less disables the check (restoring the previous, unbounded
+ * behaviour). Defaults to
+ * {@link org.apache.groovy.json.internal.BaseJsonParser#DEFAULT_MAX_NUMBER_LENGTH}, and can
+ * be overridden globally with the {@code groovy.json.maxNumberLength} system property.
+ *
+ * @return the maximum number token length
+ * @since 6.0.0
+ */
+ public int getMaxNumberLength() {
+ return maxNumberLength;
+ }
+
+ /**
+ * Sets the maximum length, in characters, of a single number token the parser will accept
+ * before throwing a {@link JsonException}. A value of {@code 0} or less disables the check.
+ *
+ * @param maxNumberLength maximum number of characters in a number token
+ * @return this {@code JsonSlurper}
+ * @since 6.0.0
+ */
+ public JsonSlurper setMaxNumberLength(int maxNumberLength) {
+ this.maxNumberLength = maxNumberLength;
+ return this;
+ }
+
/**
* Parse a text representation of a JSON data structure
*
@@ -382,6 +413,7 @@ public class JsonSlurper {
default -> new JsonParserCharArray();
};
parser.setMaxNestingDepth(maxNestingDepth);
+ parser.setMaxNumberLength(maxNumberLength);
return parser;
}
@@ -435,6 +467,7 @@ public class JsonSlurper {
} else {
JsonParserUsingCharacterSource parser = new JsonParserUsingCharacterSource();
parser.setMaxNestingDepth(maxNestingDepth);
+ parser.setMaxNumberLength(maxNumberLength);
return parser.parse(file, charset);
}
}
diff --git a/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurperClassic.java b/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurperClassic.java
index e7966ff40d..887873f8ea 100644
--- a/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurperClassic.java
+++ b/subprojects/groovy-json/src/main/java/groovy/json/JsonSlurperClassic.java
@@ -37,6 +37,7 @@ import static groovy.json.JsonTokenType.CLOSE_CURLY;
import static groovy.json.JsonTokenType.COLON;
import static groovy.json.JsonTokenType.COMMA;
import static groovy.json.JsonTokenType.NULL;
+import static groovy.json.JsonTokenType.NUMBER;
import static groovy.json.JsonTokenType.OPEN_BRACKET;
import static groovy.json.JsonTokenType.OPEN_CURLY;
import static groovy.json.JsonTokenType.STRING;
@@ -93,6 +94,60 @@ public class JsonSlurperClassic {
this.maxNestingDepth = maxNestingDepth;
}
+ /**
+ * Maximum length, in characters, of a single number token accepted before a
+ * {@link JsonException} is thrown. Converting a digit run past the {@code long} range builds a
+ * {@link java.math.BigInteger} or {@link java.math.BigDecimal}, and decimal conversion is
+ * superlinear in the digit count. A value of {@code 0} or less disables the check. Defaults to
+ * {@link org.apache.groovy.json.internal.BaseJsonParser#DEFAULT_MAX_NUMBER_LENGTH}, and can be
+ * overridden globally with the {@code groovy.json.maxNumberLength} system property.
+ */
+ private int maxNumberLength = Integer.getInteger("groovy.json.maxNumberLength",
+ org.apache.groovy.json.internal.BaseJsonParser.DEFAULT_MAX_NUMBER_LENGTH);
+
+ /**
+ * Returns the maximum length permitted for a single number token.
+ *
+ * @return the maximum number token length, or a value {@code <= 0} when the check is disabled
+ * @since 6.0.0
+ */
+ public int getMaxNumberLength() {
+ return maxNumberLength;
+ }
+
+ /**
+ * Sets the maximum length, in characters, of a single number token the parser will accept
+ * before throwing a {@link JsonException}. A value of {@code 0} or less disables the check.
+ *
+ * @param maxNumberLength maximum number of characters in a number token
+ * @since 6.0.0
+ */
+ public void setMaxNumberLength(int maxNumberLength) {
+ this.maxNumberLength = maxNumberLength;
+ }
+
+ /**
+ * Reads a token's value, enforcing {@link #maxNumberLength} before a number token is converted.
+ *
+ * @param token the token to read
+ * @param lexer the lexer, used for positional detail in the error
+ * @return the token's value
+ * @throws JsonException if a number token is longer than the configured maximum
+ */
+ private Object valueOf(JsonToken token, JsonLexer lexer) {
+ if (maxNumberLength > 0 && token.getType() == NUMBER) {
+ int length = token.getText().length();
+ if (length > maxNumberLength) {
+ throw new JsonException(
+ "JSON number length of " + length + " exceeds the maximum of " + maxNumberLength +
+ " on line: " + lexer.getReader().getLine() + ", " +
+ "column: " + lexer.getReader().getColumn() + "."
+ );
+ }
+ }
+ return token.getValue();
+ }
+
/**
* Parse a text representation of a JSON data structure
*
@@ -338,7 +393,7 @@ public class JsonSlurperClassic {
} else if (currentToken.getType() == OPEN_BRACKET) {
content.add(parseArray(lexer, depth + 1));
} else if (currentToken.getType().ordinal() >= NULL.ordinal()) {
- content.add(currentToken.getValue());
+ content.add(valueOf(currentToken, lexer));
} else if (currentToken.getType() == CLOSE_BRACKET) {
return content;
} else {
@@ -417,7 +472,7 @@ public class JsonSlurperClassic {
);
}
- String mapKey = (String) currentToken.getValue();
+ String mapKey = (String) valueOf(currentToken, lexer);
currentToken = lexer.nextToken();
@@ -459,7 +514,7 @@ public class JsonSlurperClassic {
} else if (currentToken.getType() == OPEN_BRACKET) {
content.put(mapKey, parseArray(lexer, depth + 1));
} else if (currentToken.getType().ordinal() >= NULL.ordinal()) {
- content.put(mapKey, currentToken.getValue());
+ content.put(mapKey, valueOf(currentToken, lexer));
} else {
throw new JsonException(
"Expected a value, an array, or an object " +
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/BaseJsonParser.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/BaseJsonParser.java
index be07a5cf1b..6fd4b3c11c 100644
--- a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/BaseJsonParser.java
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/BaseJsonParser.java
@@ -149,6 +149,23 @@ public abstract class BaseJsonParser implements JsonParser {
*/
protected int maxNestingDepth = Integer.getInteger("groovy.json.maxNestingDepth", DEFAULT_MAX_NESTING_DEPTH);
+ /**
+ * Default maximum length, in characters, of a single JSON number token. Converting a digit
+ * run past the {@code long} range builds a {@link java.math.BigInteger} (or
+ * {@link java.math.BigDecimal}), and decimal conversion is superlinear in the digit count,
+ * so an uncapped token lets a small document cost quadratic CPU. Chosen to match Jackson's
+ * {@code StreamReadConstraints} default, while sitting far above any realistic number.
+ */
+ public static final int DEFAULT_MAX_NUMBER_LENGTH = 1000;
+
+ /**
+ * Maximum length permitted for a single number token. A value of {@code 0} or less disables
+ * the check (restoring the previous, unbounded behaviour). Defaults to
+ * {@link #DEFAULT_MAX_NUMBER_LENGTH}, overridable globally via the
+ * {@code groovy.json.maxNumberLength} system property.
+ */
+ protected int maxNumberLength = Integer.getInteger("groovy.json.maxNumberLength", DEFAULT_MAX_NUMBER_LENGTH);
+
/**
* Current nesting depth while decoding; incremented on entry to an array/object and
* decremented on exit.
@@ -238,6 +255,35 @@ public abstract class BaseJsonParser implements JsonParser {
this.maxNestingDepth = maxNestingDepth;
}
+ /**
+ * Returns the maximum length permitted for a single number token.
+ *
+ * @return the maximum number length, or a value {@code <= 0} when the check is disabled
+ */
+ public int getMaxNumberLength() {
+ return maxNumberLength;
+ }
+
+ /**
+ * Sets the maximum length permitted for a single number token. A value of {@code 0} or less
+ * disables the check.
+ *
+ * @param maxNumberLength maximum number of characters in a number token
+ */
+ public void setMaxNumberLength(int maxNumberLength) {
+ this.maxNumberLength = maxNumberLength;
+ }
+
+ /**
+ * Enforces {@link #maxNumberLength} against a number token about to be converted.
+ *
+ * @param length the length, in characters, of the number token
+ * @throws JsonException if the configured maximum number length would be exceeded
+ */
+ protected final void checkNumberLength(int length) {
+ CharScanner.checkNumberLength(length, maxNumberLength);
+ }
+
/**
* Records entry into a nested array or object, enforcing {@link #maxNestingDepth}.
*
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java
index e081c74fcd..6788084c70 100644
--- a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/CharScanner.java
@@ -18,6 +18,8 @@
*/
package org.apache.groovy.json.internal;
+import groovy.json.JsonException;
+
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -459,6 +461,39 @@ public class CharScanner {
* @return the parsed {@link Number}
*/
public static Number parseJsonNumber(char[] buffer, int from, int max, int[] size) {
+ return parseJsonNumber(buffer, from, max, size, 0);
+ }
+
+ /**
+ * Enforces a maximum length for a single number token, ahead of the conversion that would
+ * otherwise pay for it. {@link BigInteger}/{@link BigDecimal} decimal conversion is
+ * superlinear in the digit count, so an unbounded token turns a small document into
+ * quadratic CPU.
+ *
+ * @param length the length, in characters, of the number token
+ * @param maxNumberLength the maximum permitted length; {@code 0} or less disables the check
+ * @throws JsonException if the token is longer than {@code maxNumberLength}
+ */
+ static void checkNumberLength(int length, int maxNumberLength) {
+ if (maxNumberLength > 0 && length > maxNumberLength) {
+ throw new JsonException("JSON number length of " + length
+ + " exceeds the maximum of " + maxNumberLength);
+ }
+ }
+
+ /**
+ * Parses a JSON number and optionally reports the stopping index, bounding the length of the
+ * number token.
+ *
+ * @param buffer the character array containing the number
+ * @param from the inclusive range start
+ * @param max the exclusive scan limit
+ * @param size optional single-element output array receiving the stopping index
+ * @param maxNumberLength the maximum permitted token length; {@code 0} or less disables the check
+ * @return the parsed {@link Number}
+ * @throws JsonException if the number token is longer than {@code maxNumberLength}
+ */
+ public static Number parseJsonNumber(char[] buffer, int from, int max, int[] size, int maxNumberLength) {
Number value;
boolean simple = true;
int digitsPastPoint = 0;
@@ -499,6 +534,8 @@ public class CharScanner {
final int length = index - from;
+ checkNumberLength(length, maxNumberLength);
+
try {
if (!foundDot && simple) {
if (isInteger(buffer, from, length)) {
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonFastParser.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonFastParser.java
index 860f446a4e..173de09c9e 100644
--- a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonFastParser.java
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonFastParser.java
@@ -242,6 +242,10 @@ public class JsonFastParser extends JsonParserCharArray {
Type type = doubleFloat ? Type.DOUBLE : Type.INTEGER;
+ // Checked here rather than in NumberValue: the overlay defers conversion to first access,
+ // and a document over the limit should be rejected by the parse that read it.
+ checkNumberLength(__index - startIndex);
+
return new NumberValue(chop, type, startIndex, __index, this.charArray);
}
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserCharArray.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserCharArray.java
index 779e455492..642b4f3415 100644
--- a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserCharArray.java
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserCharArray.java
@@ -250,7 +250,7 @@ public class JsonParserCharArray extends BaseJsonParser {
int[] endIndex = new int[1];
private Object decodeNumber() {
- Number num = CharScanner.parseJsonNumber(charArray, __index, charArray.length, endIndex);
+ Number num = CharScanner.parseJsonNumber(charArray, __index, charArray.length, endIndex, maxNumberLength);
__index = endIndex[0];
return num;
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserLax.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserLax.java
index 4ece265ba0..1b54cd8018 100644
--- a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserLax.java
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserLax.java
@@ -476,6 +476,10 @@ public class JsonParserLax extends JsonParserCharArray {
Type type = doubleFloat ? Type.DOUBLE : Type.INTEGER;
+ // Checked here rather than in NumberValue: the overlay defers conversion to first access,
+ // and a document over the limit should be rejected by the parse that read it.
+ checkNumberLength(__index - startIndex);
+
return new NumberValue(chop, type, startIndex, __index, this.charArray);
}
diff --git a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserUsingCharacterSource.java b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserUsingCharacterSource.java
index a4fb245300..d00fb695df 100644
--- a/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserUsingCharacterSource.java
+++ b/subprojects/groovy-json/src/main/java/org/apache/groovy/json/internal/JsonParserUsingCharacterSource.java
@@ -147,6 +147,7 @@ public class JsonParserUsingCharacterSource extends BaseJsonParser {
private Object decodeNumber(boolean negative) {
char[] chars = characterSource.readNumber();
+ checkNumberLength(chars.length);
Object value;
try {
diff --git a/subprojects/groovy-json/src/test/groovy/groovy/json/JsonSlurperMaxNumberLengthTest.groovy b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonSlurperMaxNumberLengthTest.groovy
new file mode 100644
index 0000000000..aded382893
--- /dev/null
+++ b/subprojects/groovy-json/src/test/groovy/groovy/json/JsonSlurperMaxNumberLengthTest.groovy
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.json
+
+import org.apache.groovy.json.internal.BaseJsonParser
+import org.junit.jupiter.api.Test
+
+import static groovy.test.GroovyAssert.shouldFail
+import static org.junit.jupiter.api.Assertions.assertEquals
+import static org.junit.jupiter.api.Assertions.assertTrue
+
+/**
+ * A number token past {@code maxNumberLength} is rejected with a {@link JsonException} on every
+ * parser variant, rather than being converted by a superlinear {@code BigInteger}/{@code BigDecimal}
+ * conversion. The overlay parsers defer conversion to first access, so the check has to reject the
+ * document at parse time, not when the value is later read.
+ */
+class JsonSlurperMaxNumberLengthTest {
+
+ private static final int DEFAULT = BaseJsonParser.DEFAULT_MAX_NUMBER_LENGTH
+
+ private static String digits(int n) {
+ '9' * n
+ }
+
+ /**
+ * The CHARACTER_SOURCE parser re-wraps any exception escaping its array/object decode in a
+ * generic "Unexpected issue" JsonException, so the cap's own message can arrive as a cause.
+ * The same is true of the existing nesting-depth cap. Look through the chain rather than
+ * pinning which layer carries the text.
+ */
+ private static boolean reports(Throwable t, String text) {
+ for (Throwable c = t; c != null; c = c.cause) {
+ if (c.message?.contains(text)) return true
+ if (c.cause === c) break
+ }
+ false
+ }
+
+ // GROOVY-12329
+ @Test
+ void testDefaultCapMatchesJacksonsDefault() {
+ assertEquals(1000, DEFAULT)
+ assertEquals(DEFAULT, new JsonSlurper().maxNumberLength)
+ assertEquals(DEFAULT, new JsonSlurperClassic().maxNumberLength)
+ }
+
+ // GROOVY-12329
+ @Test
+ void testNumberAtTheLimitStillParses() {
+ JsonParserType.values().each { type ->
+ def parsed = new JsonSlurper(type: type).parseText("[${digits(DEFAULT)}]")
+ assertEquals(DEFAULT, parsed[0].toString().length(), "type ${type}".toString())
+ }
+ assertEquals(DEFAULT, new JsonSlurperClassic().parseText("[${digits(DEFAULT)}]")[0].toString().length())
+ }
+
+ // GROOVY-12329
+ @Test
+ void testOverlongIntegerRejectedOnEveryParserType() {
+ JsonParserType.values().each { type ->
+ def e = shouldFail(JsonException) {
+ // toString() would realise a deferred overlay value; the parse itself must fail
+ new JsonSlurper(type: type).parseText("[${digits(DEFAULT + 1)}]")
+ }
+ assertTrue(reports(e, 'exceeds the maximum'), "type ${type}: ${e.message}".toString())
+ }
+ }
+
+ // GROOVY-12329
+ @Test
+ void testOverlongDecimalRejectedOnEveryParserType() {
+ // BigDecimal conversion is superlinear for the same reason BigInteger is
+ JsonParserType.values().each { type ->
+ shouldFail(JsonException) {
+ new JsonSlurper(type: type).parseText("[1.${digits(DEFAULT + 1)}]")
+ }
+ }
+ }
+
+ // GROOVY-12329
+ @Test
+ void testOverlongNumberRejectedByClassicParser() {
+ def e = shouldFail(JsonException) {
+ new JsonSlurperClassic().parseText("[${digits(DEFAULT + 1)}]")
+ }
+ assertTrue(reports(e, 'exceeds the maximum'), e.message)
+ }
+
+ // GROOVY-12329
+ @Test
+ void testLimitIsConfigurablePerInstance() {
+ JsonParserType.values().each { type ->
+ def slurper = new JsonSlurper(type: type).setMaxNumberLength(10)
+ shouldFail(JsonException) { slurper.parseText("[${digits(11)}]") }
+ assertEquals(10, slurper.parseText("[${digits(10)}]")[0].toString().length())
+ }
+
+ def classic = new JsonSlurperClassic()
+ classic.maxNumberLength = 10
+ shouldFail(JsonException) { classic.parseText("[${digits(11)}]") }
+ assertEquals(10, classic.parseText("[${digits(10)}]")[0].toString().length())
+ }
+
+ // GROOVY-12329
+ @Test
+ void testCheckIsDisabledByNonPositiveLimit() {
+ JsonParserType.values().each { type ->
+ def slurper = new JsonSlurper(type: type).setMaxNumberLength(0)
+ assertEquals(DEFAULT + 50, slurper.parseText("[${digits(DEFAULT + 50)}]")[0].toString().length(),
+ "type ${type}".toString())
+ }
+
+ def classic = new JsonSlurperClassic()
+ classic.maxNumberLength = -1
+ assertEquals(DEFAULT + 50, classic.parseText("[${digits(DEFAULT + 50)}]")[0].toString().length())
+ }
+
+ // GROOVY-12329
+ @Test
+ void testOrdinaryNumbersAreUnaffected() {
+ JsonParserType.values().each { type ->
+ def parsed = new JsonSlurper(type: type).parseText('{"a":1,"b":-2.5,"c":6.02e23,"d":9223372036854775808}')
+ assertEquals(1, parsed.a as int, "type ${type}".toString())
+ assertEquals(-2.5, parsed.b as double, 0.0, "type ${type}".toString())
+ assertEquals(6.02e23, parsed.c as double, 0.0, "type ${type}".toString())
+ assertEquals(new BigInteger('9223372036854775808'), parsed.d as BigInteger, "type ${type}".toString())
+ }
+ }
+}