From 2d08b82d7016055dc45d31c1d2c5254b85dab295 Mon Sep 17 00:00:00 2001 From: Seva Safris Date: Fri, 26 May 2023 20:19:40 +0700 Subject: [PATCH] Support non-strict compliance to JSON spec, re #12 --- .../java/org/openjax/json/JsonReader.java | 76 +++++++++++++++++-- src/main/java/org/openjax/json/JsonUtil.java | 7 +- .../java/org/openjax/json/JsonTypesTest.java | 4 +- 3 files changed, 76 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openjax/json/JsonReader.java b/src/main/java/org/openjax/json/JsonReader.java index 0285890..85330af 100644 --- a/src/main/java/org/openjax/json/JsonReader.java +++ b/src/main/java/org/openjax/json/JsonReader.java @@ -87,33 +87,57 @@ public static JsonReader of(final Reader reader) { private int nextStart = 0; private final boolean ignoreWhitespace; + private final boolean strict; /** - * Construct a new {@link JsonReader} for JSON content to be read from the specified {@link Reader}, that ignores inter-token - * whitespace. This constructor is equivalent to calling {@code new JsonReader(reader, true)}. + * Construct a new {@link JsonReader} for JSON content to be read from the specified {@link Reader}, that ignores inter-token + * whitespace, and enforces strict compliance to the JSON + * Specification. + *

+ * This constructor is equivalent to calling {@code new JsonReader(reader, true, true)}. * * @param reader The {@link Reader} from which JSON is to be read. * @throws NullPointerException If {@code reader} is null. */ public JsonReader(final Reader reader) { - this(reader, true); + this(reader, true, true); } /** - * Construct a new {@link JsonReader} for JSON content to be read from the specified {@link Reader}. + * Construct a new {@link JsonReader} for JSON content to be read from the specified {@link Reader}, that enforces strict + * compliance to the JSON Specification. + *

+ * This constructor is equivalent to calling {@code new JsonReader(reader, ignoreWhitespace, true)}. * * @param reader The {@link Reader} from which JSON is to be read. * @param ignoreWhitespace If {@code ignoreWhitespace == false}, inter-token whitespace will not be skipped. * @throws NullPointerException If {@code reader} is null. */ public JsonReader(final Reader reader, final boolean ignoreWhitespace) { + this(reader, ignoreWhitespace, true); + } + + /** + * Construct a new {@link JsonReader} for JSON content to be read from the specified {@link Reader}. + * + * @param reader The {@link Reader} from which JSON is to be read. + * @param ignoreWhitespace If {@code ignoreWhitespace == false}, inter-token whitespace will not be skipped. + * @param strict Whether to enforce strict compliance to the JSON Specification + * while parsing the JSON document. + * @throws NullPointerException If {@code reader} is null. + */ + public JsonReader(final Reader reader, final boolean ignoreWhitespace, final boolean strict) { super(reader, DEFAULT_BUFFER_SIZE); this.ignoreWhitespace = ignoreWhitespace; + this.strict = strict; } /** * Construct a new {@link JsonReader} for JSON content to be read from the specified string, that ignores inter-token - * whitespace. This constructor is equivalent to calling {@code new JsonReader(str, true)}. + * whitespace, and enforces strict compliance to the JSON + * Specification. + *

+ * This constructor is equivalent to calling {@code new JsonReader(str, true, true)}. * * @param str The string with the JSON document to be read. * @throws NullPointerException If {@code str} is null. @@ -123,15 +147,52 @@ public JsonReader(final String str) { } /** - * Construct a new {@link JsonReader} for JSON content to be read from the specified string. + * Construct a new {@link JsonReader} for JSON content to be read from the specified string, that enforces strict compliance to + * the JSON Specification. + *

+ * This constructor is equivalent to calling {@code new JsonReader(str, ignoreWhitespace, true)}. * * @param str The string with the JSON document to be read. * @param ignoreWhitespace If {@code ignoreWhitespace == false}, inter-token whitespace will not be skipped. * @throws NullPointerException If {@code str} is null. */ public JsonReader(final String str, final boolean ignoreWhitespace) { + this(str, ignoreWhitespace, true); + } + + /** + * Construct a new {@link JsonReader} for JSON content to be read from the specified string. + * + * @param str The string with the JSON document to be read. + * @param strict Whether to enforce strict compliance to the JSON Specification + * while parsing the JSON document. + * @param ignoreWhitespace If {@code ignoreWhitespace == false}, inter-token whitespace will not be skipped. + * @throws NullPointerException If {@code str} is null. + */ + public JsonReader(final String str, final boolean ignoreWhitespace, final boolean strict) { super(new UnsynchronizedStringReader(str), DEFAULT_BUFFER_SIZE); this.ignoreWhitespace = ignoreWhitespace; + this.strict = strict; + } + + /** + * Returns whether this {@link JsonReader} skips inter-token whitespace. + * + * @return Whether this {@link JsonReader} skips inter-token whitespace. + */ + public boolean isIgnoreWhitespace() { + return ignoreWhitespace; + } + + /** + * Returns whether this {@link JsonReader} enforces strict compliance to the JSON + * Specification. + * + * @return Whether this {@link JsonReader} enforces strict compliance to the JSON + * Specification. + */ + public boolean isStrict() { + return strict; } /** @@ -826,7 +887,8 @@ else if (ch < '0' || '9' < ch) { break; } else if (prev == '0' && i == (first == '~' ? 2 : 1)) { - throw new JsonParseException("Leading zeros are not allowed", getPosition() - 2); + if (strict) + throw new JsonParseException("Leading zeros are not allowed", getPosition() - 2); } } diff --git a/src/main/java/org/openjax/json/JsonUtil.java b/src/main/java/org/openjax/json/JsonUtil.java index 199d29c..c449f56 100644 --- a/src/main/java/org/openjax/json/JsonUtil.java +++ b/src/main/java/org/openjax/json/JsonUtil.java @@ -64,6 +64,8 @@ public static boolean isWhitespace(final int ch) { * @param The type parameter for the return instance. * @param type The class of the return instance. * @param str The {@link CharSequence} to parse. + * @param strict Whether to enforce strict compliance to the JSON Specification + * while parsing the JSON document. * @return An instance of class {@code type} representing the parsed string. * @throws JsonParseException If a parsing error has occurred. * @throws IllegalArgumentException If the specified string is empty, or if an instance of the specific class type does not define @@ -71,7 +73,7 @@ public static boolean isWhitespace(final int ch) { * @throws NullPointerException If {@code str} is null. */ @SuppressWarnings("unchecked") - public static T parseNumber(final Class type, CharSequence str) throws JsonParseException { + public static T parseNumber(final Class type, CharSequence str, final boolean strict) throws JsonParseException { if (str.length() == 0) throw new IllegalArgumentException("Empty string"); @@ -122,7 +124,8 @@ else if (ch < '0' || '9' < ch) { break; } else if (last == '0' && i == expStart + (first == '~' ? 2 : 1)) { - throw new JsonParseException("Leading zeros are not allowed", i - 1); + if (strict) + throw new JsonParseException("Leading zeros are not allowed", i - 1); } } diff --git a/src/test/java/org/openjax/json/JsonTypesTest.java b/src/test/java/org/openjax/json/JsonTypesTest.java index c80be67..7bca833 100644 --- a/src/test/java/org/openjax/json/JsonTypesTest.java +++ b/src/test/java/org/openjax/json/JsonTypesTest.java @@ -37,12 +37,12 @@ public void testIsWhitespace() { } private static void testPass(final String number, final Class type) { - JsonUtil.parseNumber(type, number); + JsonUtil.parseNumber(type, number, true); } private static void testFail(final Class cls, final String number) { try { - JsonUtil.parseNumber(BigDecimal.class, number); + JsonUtil.parseNumber(BigDecimal.class, number, true); fail("Expected " + cls.getSimpleName()); } catch (final Exception e) {