mirror of
https://github.com/TeamNewPipe/nanojson
synced 2025-10-06 00:13:15 +02:00
Merge pull request #27 from TeamNewPipe/revert-our-changes-and-use-firemasterk
This commit is contained in:
54
.github/workflows/codeql-analysis.yml
vendored
54
.github/workflows/codeql-analysis.yml
vendored
@@ -1,54 +0,0 @@
|
|||||||
name: "Code scanning - action"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master, ]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [master]
|
|
||||||
schedule:
|
|
||||||
- cron: '0 11 * * 5'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
CodeQL-Build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
# We must fetch at least the immediate parents so that if this is
|
|
||||||
# a pull request then we can checkout the head.
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
# If this run was triggered by a pull request event, then checkout
|
|
||||||
# the head of the pull request instead of the merge commit.
|
|
||||||
- run: git checkout HEAD^2
|
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v1
|
|
||||||
# Override language selection by uncommenting this and choosing your languages
|
|
||||||
# with:
|
|
||||||
# languages: go, javascript, csharp, python, cpp, java
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v1
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v1
|
|
31
.github/workflows/maven.yml
vendored
Normal file
31
.github/workflows/maven.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
|
||||||
|
|
||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
name: Java CI with Maven
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up JDK 11
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: '17'
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: maven
|
||||||
|
- name: Build with Maven
|
||||||
|
run: mvn -B package --file pom.xml
|
@@ -2,6 +2,5 @@
|
|||||||
|
|
||||||
This is a fork of [nanojson](https://github.com/mmastrac/nanojson) for use by NewPipe(Extractor). It has the following changes:
|
This is a fork of [nanojson](https://github.com/mmastrac/nanojson) for use by NewPipe(Extractor). It has the following changes:
|
||||||
|
|
||||||
- It returns an empty `JsonObject` or `JsonArray` by default instead of `null`, preventing `NullPointerException`s.
|
|
||||||
- It accepts JS-like JSON, such as `{ this: 'is', an: 'example' }`.
|
|
||||||
- Added ``JsonArray#streamAs`` and ``JsonArray#streamAsJsonObjects`` shortcut methods.
|
- Added ``JsonArray#streamAs`` and ``JsonArray#streamAsJsonObjects`` shortcut methods.
|
||||||
|
- Various performance improvements borrowed from [@FireMasterK's fork](https://github.com/FireMasterK/nanojson).
|
||||||
|
@@ -20,8 +20,7 @@
|
|||||||
</module>
|
</module>
|
||||||
|
|
||||||
<module name="LineLength">
|
<module name="LineLength">
|
||||||
<property name="max" value="120" />
|
<property name="max" value="150" />
|
||||||
<property name="tabWidth" value="4" />
|
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
<!-- If you set the basedir property below, then all reported file names
|
<!-- If you set the basedir property below, then all reported file names
|
||||||
@@ -70,7 +69,7 @@
|
|||||||
<!-- Checks for Javadoc comments. -->
|
<!-- Checks for Javadoc comments. -->
|
||||||
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
|
||||||
<module name="JavadocMethod">
|
<module name="JavadocMethod">
|
||||||
<property name="scope" value="public" />
|
<property name="accessModifiers" value="public" />
|
||||||
<property name="allowMissingParamTags" value="true" />
|
<property name="allowMissingParamTags" value="true" />
|
||||||
<property name="allowMissingReturnTag" value="true" />
|
<property name="allowMissingReturnTag" value="true" />
|
||||||
</module>
|
</module>
|
||||||
|
95
pom.xml
95
pom.xml
@@ -13,19 +13,35 @@
|
|||||||
<artifactId>nanojson</artifactId>
|
<artifactId>nanojson</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>nanojson</name>
|
<name>nanojson</name>
|
||||||
<version>1.8-SNAPSHOT</version>
|
<version>1.11-SNAPSHOT</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
</properties>
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.13.4</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.randelshofer</groupId>
|
||||||
|
<artifactId>fastdoubleparser</artifactId>
|
||||||
|
<version>2.0.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>3.11.3</version>
|
||||||
<reportSets>
|
<reportSets>
|
||||||
<reportSet>
|
<reportSet>
|
||||||
<id>html</id>
|
<id>html</id>
|
||||||
@@ -44,7 +60,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.6.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<configLocation>checkstyle.xml</configLocation>
|
<configLocation>checkstyle.xml</configLocation>
|
||||||
<propertyExpansion>basedir=${basedir}</propertyExpansion>
|
<propertyExpansion>basedir=${basedir}</propertyExpansion>
|
||||||
@@ -66,7 +82,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||||
<version>2.22.2</version>
|
<version>3.5.3</version>
|
||||||
<reportSets>
|
<reportSets>
|
||||||
<reportSet>
|
<reportSet>
|
||||||
<reports>
|
<reports>
|
||||||
@@ -77,21 +93,12 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</reporting>
|
</reporting>
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.13.2</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
<version>3.2.1</version>
|
<version>3.3.1</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>attach-sources</id>
|
<id>attach-sources</id>
|
||||||
@@ -104,7 +111,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
<version>3.3.0</version>
|
<version>3.11.3</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>attach-javadocs</id>
|
<id>attach-javadocs</id>
|
||||||
@@ -117,7 +124,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
<version>3.1.2</version>
|
<version>3.6.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<configLocation>checkstyle.xml</configLocation>
|
<configLocation>checkstyle.xml</configLocation>
|
||||||
<propertyExpansion>basedir=${basedir}</propertyExpansion>
|
<propertyExpansion>basedir=${basedir}</propertyExpansion>
|
||||||
@@ -145,7 +152,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>exec-maven-plugin</artifactId>
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
<version>3.0.0</version>
|
<version>3.5.1</version>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<executable>java</executable>
|
<executable>java</executable>
|
||||||
@@ -165,15 +172,10 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.14.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<debug>true</debug>
|
<debug>false</debug>
|
||||||
<debuglevel>none</debuglevel>
|
<release>11</release>
|
||||||
<source>1.8</source>
|
|
||||||
<target>1.8</target>
|
|
||||||
<archive>
|
|
||||||
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
|
|
||||||
</archive>
|
|
||||||
<compilerArgument>-Xlint:all</compilerArgument>
|
<compilerArgument>-Xlint:all</compilerArgument>
|
||||||
<compilerArguments>
|
<compilerArguments>
|
||||||
<Werror />
|
<Werror />
|
||||||
@@ -183,20 +185,43 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-site-plugin</artifactId>
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
<version>3.9.1</version>
|
<version>3.21.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>biz.aQute.bnd</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>bnd-maven-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>jar</id>
|
||||||
|
<goals>
|
||||||
|
<goal>jar</goal>
|
||||||
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<bnd><![CDATA[
|
||||||
<manifestEntries>
|
Bundle-SymbolicName: ${groupId}.${artifactId}
|
||||||
<Automatic-Module-Name>com.grack.nanojson</Automatic-Module-Name>
|
Export-Package: com.grack.nanojson
|
||||||
</manifestEntries>
|
-jpms-module-info: com.grack.nanojson
|
||||||
</archive>
|
-noextraheaders:
|
||||||
|
-removeheaders: \
|
||||||
|
Tool, \
|
||||||
|
Bnd-LastModified, \
|
||||||
|
Bnd-ManifestVersion, \
|
||||||
|
Build-Jdk, \
|
||||||
|
Built-By, \
|
||||||
|
Created-By, \
|
||||||
|
Private-Package, \
|
||||||
|
Bundle-DocURL, \
|
||||||
|
Bundle-Name, \
|
||||||
|
Bundle-Vendor,\
|
||||||
|
Bundle-Description,\
|
||||||
|
Bundle-SCM
|
||||||
|
]]></bnd>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
@@ -210,7 +235,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.sonatype.plugins</groupId>
|
<groupId>org.sonatype.plugins</groupId>
|
||||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||||
<version>1.6.8</version>
|
<version>1.7.0</version>
|
||||||
<extensions>true</extensions>
|
<extensions>true</extensions>
|
||||||
<configuration>
|
<configuration>
|
||||||
<serverId>sonatype-nexus-staging</serverId>
|
<serverId>sonatype-nexus-staging</serverId>
|
||||||
@@ -221,7 +246,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
<version>3.0.1</version>
|
<version>3.2.8</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<id>sign-artifacts</id>
|
<id>sign-artifacts</id>
|
||||||
|
69
src/main/java/com/grack/nanojson/CharBufferPool.java
Normal file
69
src/main/java/com/grack/nanojson/CharBufferPool.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 The nanojson Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 com.grack.nanojson;
|
||||||
|
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public final class CharBufferPool {
|
||||||
|
|
||||||
|
private static final AtomicInteger SIZE = new AtomicInteger(0);
|
||||||
|
private static final int MAX_SIZE = 1000;
|
||||||
|
private static final int MAX_RETAINED_BUFFER_SIZE = 16 * 1024; // 16KB limit for pooled buffers
|
||||||
|
|
||||||
|
private static final PriorityQueue<CharBuffer> BUFFERS = new PriorityQueue<>();
|
||||||
|
|
||||||
|
private CharBufferPool() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CharBuffer get(int capacity) {
|
||||||
|
synchronized (BUFFERS) {
|
||||||
|
if (!BUFFERS.isEmpty()) {
|
||||||
|
CharBuffer buffer = BUFFERS.poll();
|
||||||
|
if (buffer.capacity() < capacity) {
|
||||||
|
return CharBuffer.allocate(capacity);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SIZE.incrementAndGet() > MAX_SIZE) {
|
||||||
|
SIZE.decrementAndGet();
|
||||||
|
throw new IllegalStateException("Buffer pool size limit exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CharBuffer.allocate(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(CharBuffer buffer) {
|
||||||
|
if (buffer == null || buffer.capacity() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.limit() > MAX_RETAINED_BUFFER_SIZE) {
|
||||||
|
// If the buffer is too large, decrement SIZE so another can be created
|
||||||
|
SIZE.decrementAndGet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
synchronized (BUFFERS) {
|
||||||
|
BUFFERS.add(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -215,6 +215,8 @@ public class JsonArray extends ArrayList<Object> {
|
|||||||
*/
|
*/
|
||||||
public String getString(int key, String default_) {
|
public String getString(int key, String default_) {
|
||||||
Object o = get(key);
|
Object o = get(key);
|
||||||
|
if (o instanceof LazyString)
|
||||||
|
return o.toString();
|
||||||
if (o instanceof String)
|
if (o instanceof String)
|
||||||
return (String) o;
|
return (String) o;
|
||||||
return default_;
|
return default_;
|
||||||
@@ -252,7 +254,8 @@ public class JsonArray extends ArrayList<Object> {
|
|||||||
* Returns true if the array has a string element at that index.
|
* Returns true if the array has a string element at that index.
|
||||||
*/
|
*/
|
||||||
public boolean isString(int key) {
|
public boolean isString(int key) {
|
||||||
return get(key) instanceof String;
|
Object o = get(key);
|
||||||
|
return o instanceof LazyString || o instanceof String;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -27,6 +27,7 @@ import java.util.Stack;
|
|||||||
*/
|
*/
|
||||||
public final class JsonBuilder<T> implements JsonSink<JsonBuilder<T>> {
|
public final class JsonBuilder<T> implements JsonSink<JsonBuilder<T>> {
|
||||||
private Stack<Object> json = new Stack<>();
|
private Stack<Object> json = new Stack<>();
|
||||||
|
private String pendingKey;
|
||||||
private T root;
|
private T root;
|
||||||
|
|
||||||
JsonBuilder(T root) {
|
JsonBuilder(T root) {
|
||||||
@@ -73,12 +74,20 @@ public final class JsonBuilder<T> implements JsonSink<JsonBuilder<T>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonBuilder<T> value(Object o) {
|
public JsonBuilder<T> value(Object o) {
|
||||||
|
if (pendingKey != null) {
|
||||||
|
obj().put(pendingKey, o);
|
||||||
|
pendingKey = null;
|
||||||
|
} else {
|
||||||
arr().add(o);
|
arr().add(o);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonBuilder<T> value(String key, Object o) {
|
public JsonBuilder<T> value(String key, Object o) {
|
||||||
|
if (pendingKey != null) {
|
||||||
|
throw new JsonWriterException("Invalid call to emit a key value immediately after emitting a key");
|
||||||
|
}
|
||||||
obj().put(key, o);
|
obj().put(key, o);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -193,6 +202,18 @@ public final class JsonBuilder<T> implements JsonSink<JsonBuilder<T>> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JsonBuilder<T> key(String key) {
|
||||||
|
if (key == null)
|
||||||
|
throw new NullPointerException("key");
|
||||||
|
if (!(json.peek() instanceof JsonObject))
|
||||||
|
throw new JsonWriterException("Invalid call to emit a key value while not writing an object");
|
||||||
|
if (pendingKey != null)
|
||||||
|
throw new JsonWriterException("Invalid call to emit a key value immediately after emitting a key");
|
||||||
|
pendingKey = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private JsonObject obj() {
|
private JsonObject obj() {
|
||||||
try {
|
try {
|
||||||
return (JsonObject)json.peek();
|
return (JsonObject)json.peek();
|
||||||
|
13
src/main/java/com/grack/nanojson/JsonConvertible.java
Normal file
13
src/main/java/com/grack/nanojson/JsonConvertible.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.grack.nanojson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for classes that can be converted into valid JSON values.
|
||||||
|
*/
|
||||||
|
public interface JsonConvertible {
|
||||||
|
/**
|
||||||
|
* Creates a view of this object as a valid JSON Type.
|
||||||
|
*
|
||||||
|
* @return an instance of Map, Collection, String, Number or Boolean or {@code null}
|
||||||
|
*/
|
||||||
|
Object toJsonValue();
|
||||||
|
}
|
@@ -15,45 +15,52 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser;
|
||||||
|
import ch.randelshofer.fastdoubleparser.JavaDoubleParser;
|
||||||
|
import ch.randelshofer.fastdoubleparser.JavaFloatParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazily-parsed number for performance.
|
* Lazily-parsed number for performance.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
class JsonLazyNumber extends Number {
|
class JsonLazyNumber extends Number {
|
||||||
private String value;
|
private char[] value;
|
||||||
private boolean isDouble;
|
private boolean isDouble;
|
||||||
|
|
||||||
JsonLazyNumber(String number, boolean isDoubleValue) {
|
JsonLazyNumber(char[] number, boolean isDoubleValue) {
|
||||||
this.value = number;
|
this.value = number;
|
||||||
this.isDouble = isDoubleValue;
|
this.isDouble = isDoubleValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double doubleValue() {
|
public double doubleValue() {
|
||||||
return Double.parseDouble(value);
|
return JavaDoubleParser.parseDouble(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float floatValue() {
|
public float floatValue() {
|
||||||
return Float.parseFloat(value);
|
return JavaFloatParser.parseFloat(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int intValue() {
|
public int intValue() {
|
||||||
return isDouble ? (int)Double.parseDouble(value) : Integer.parseInt(value);
|
return isDouble ? (int)JavaDoubleParser.parseDouble(value) : Integer.parseInt(new String(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long longValue() {
|
public long longValue() {
|
||||||
return isDouble ? (long)Double.parseDouble(value) : Long.parseLong(value);
|
return isDouble ? (long) JavaDoubleParser.parseDouble(value) : Long.parseLong(new String(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new String(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Avoid serializing {@link JsonLazyNumber}.
|
* Avoid serializing {@link JsonLazyNumber}.
|
||||||
*/
|
*/
|
||||||
private Object writeReplace() {
|
private Object writeReplace() {
|
||||||
return new BigDecimal(value);
|
return JavaBigDecimalParser.parseBigDecimal(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -206,6 +206,8 @@ public class JsonObject extends LinkedHashMap<String, Object> {
|
|||||||
*/
|
*/
|
||||||
public String getString(String key, String default_) {
|
public String getString(String key, String default_) {
|
||||||
Object o = get(key);
|
Object o = get(key);
|
||||||
|
if (o instanceof LazyString)
|
||||||
|
return o.toString();
|
||||||
if (o instanceof String)
|
if (o instanceof String)
|
||||||
return (String) o;
|
return (String) o;
|
||||||
return default_;
|
return default_;
|
||||||
@@ -243,6 +245,7 @@ public class JsonObject extends LinkedHashMap<String, Object> {
|
|||||||
* Returns true if the object has a string element at that key.
|
* Returns true if the object has a string element at that key.
|
||||||
*/
|
*/
|
||||||
public boolean isString(String key) {
|
public boolean isString(String key) {
|
||||||
return get(key) instanceof String;
|
Object o = get(key);
|
||||||
|
return o instanceof LazyString || o instanceof String;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,12 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
|
import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser;
|
||||||
|
import ch.randelshofer.fastdoubleparser.JavaDoubleParser;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple JSON parser.
|
* Simple JSON parser.
|
||||||
@@ -36,18 +39,21 @@ public final class JsonParser {
|
|||||||
private Object value;
|
private Object value;
|
||||||
private int token;
|
private int token;
|
||||||
|
|
||||||
private JsonTokener tokener;
|
private final JsonTokener tokener;
|
||||||
private boolean lazyNumbers;
|
private final boolean lazyNumbers;
|
||||||
|
private final boolean lazyStrings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a type-safe parser context for a {@link JsonObject}, {@link JsonArray} or "any" type from which you can
|
* Returns a type-safe parser context for a {@link JsonObject},
|
||||||
|
* {@link JsonArray} or "any" type from which you can
|
||||||
* parse a {@link String} or a {@link Reader}.
|
* parse a {@link String} or a {@link Reader}.
|
||||||
*
|
*
|
||||||
* @param <T> The parsed type.
|
* @param <T> The parsed type.
|
||||||
*/
|
*/
|
||||||
public static final class JsonParserContext<T> {
|
public static final class JsonParserContext<T> {
|
||||||
private final Class<T> clazz;
|
private final Class<T> clazz;
|
||||||
private boolean lazyNumbers;
|
private boolean lazyNumbers = true;
|
||||||
|
private boolean lazyStrings = true;
|
||||||
|
|
||||||
JsonParserContext(Class<T> clazz) {
|
JsonParserContext(Class<T> clazz) {
|
||||||
this.clazz = clazz;
|
this.clazz = clazz;
|
||||||
@@ -62,18 +68,27 @@ public final class JsonParser {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses Strings lazily, allowing us to defer some of the cost of String
|
||||||
|
* construction until later.
|
||||||
|
*/
|
||||||
|
public JsonParserContext<T> withLazyStrings() {
|
||||||
|
lazyStrings = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current JSON type from a {@link String}.
|
* Parses the current JSON type from a {@link String}.
|
||||||
*/
|
*/
|
||||||
public T from(String s) throws JsonParserException {
|
public T from(String s) throws JsonParserException {
|
||||||
return new JsonParser(new JsonTokener(new StringReader(s)), lazyNumbers).parse(clazz);
|
return new JsonParser(new JsonTokener(new StringReader(s)), lazyNumbers, lazyStrings).parse(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current` JSON type from a {@link Reader}.
|
* Parses the current` JSON type from a {@link Reader}.
|
||||||
*/
|
*/
|
||||||
public T from(Reader r) throws JsonParserException {
|
public T from(Reader r) throws JsonParserException {
|
||||||
return new JsonParser(new JsonTokener(r), lazyNumbers).parse(clazz);
|
return new JsonParser(new JsonTokener(r), lazyNumbers, lazyStrings).parse(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,11 +96,8 @@ public final class JsonParser {
|
|||||||
*/
|
*/
|
||||||
public T from(URL url) throws JsonParserException {
|
public T from(URL url) throws JsonParserException {
|
||||||
try {
|
try {
|
||||||
InputStream stm = url.openStream();
|
try (InputStream stm = url.openStream()) {
|
||||||
try {
|
|
||||||
return from(stm);
|
return from(stm);
|
||||||
} finally {
|
|
||||||
stm.close();
|
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new JsonParserException(e, "IOException opening URL", 1, 1, 0);
|
throw new JsonParserException(e, "IOException opening URL", 1, 1, 0);
|
||||||
@@ -93,16 +105,18 @@ public final class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current JSON type from a {@link InputStream}. Detects the encoding from the input stream.
|
* Parses the current JSON type from a {@link InputStream}. Detects the encoding
|
||||||
|
* from the input stream.
|
||||||
*/
|
*/
|
||||||
public T from(InputStream stm) throws JsonParserException {
|
public T from(InputStream stm) throws JsonParserException {
|
||||||
return new JsonParser(new JsonTokener(stm), lazyNumbers).parse(clazz);
|
return new JsonParser(new JsonTokener(stm), lazyNumbers, lazyStrings).parse(clazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonParser(JsonTokener tokener, boolean lazyNumbers) throws JsonParserException {
|
JsonParser(JsonTokener tokener, boolean lazyNumbers, boolean lazyStrings) throws JsonParserException {
|
||||||
this.tokener = tokener;
|
this.tokener = tokener;
|
||||||
this.lazyNumbers = lazyNumbers;
|
this.lazyNumbers = lazyNumbers;
|
||||||
|
this.lazyStrings = lazyStrings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,8 +142,10 @@ public final class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses any object from a source. For any valid JSON, returns either a null (for the JSON string 'null'), a
|
* Parses any object from a source. For any valid JSON, returns either a null
|
||||||
* {@link String}, a {@link Number}, a {@link Boolean}, a {@link JsonObject} or a {@link JsonArray}.
|
* (for the JSON string 'null'), a
|
||||||
|
* {@link String}, a {@link Number}, a {@link Boolean}, a {@link JsonObject} or
|
||||||
|
* a {@link JsonArray}.
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* Object json = {@link JsonParser}.any().from("{\"a\":[true,false], \"b\":1}");
|
* Object json = {@link JsonParser}.any().from("{\"a\":[true,false], \"b\":1}");
|
||||||
@@ -144,82 +160,77 @@ public final class JsonParser {
|
|||||||
* Parse a single JSON value from the string, expecting an EOF at the end.
|
* Parse a single JSON value from the string, expecting an EOF at the end.
|
||||||
*/
|
*/
|
||||||
<T> T parse(Class<T> clazz) throws JsonParserException {
|
<T> T parse(Class<T> clazz) throws JsonParserException {
|
||||||
advanceToken(false, false);
|
try {
|
||||||
|
advanceToken();
|
||||||
Object parsed = currentValue();
|
Object parsed = currentValue();
|
||||||
if (advanceToken(false, false) != JsonTokener.TOKEN_EOF)
|
if (advanceToken() != JsonTokener.TOKEN_EOF)
|
||||||
throw tokener.createParseException(null, "Expected end of input, got " + token, true);
|
throw tokener.createParseException(null, "Expected end of input, got " + token, true);
|
||||||
if (clazz != Object.class && (parsed == null || !clazz.isAssignableFrom(parsed.getClass())))
|
if (clazz != Object.class && (parsed == null || !clazz.isAssignableFrom(parsed.getClass())))
|
||||||
throw tokener.createParseException(null,
|
throw tokener.createParseException(null,
|
||||||
"JSON did not contain the correct type, expected " + clazz.getSimpleName() + ".",
|
"JSON did not contain the correct type, expected " + clazz.getSimpleName() + ".",
|
||||||
true);
|
true);
|
||||||
return clazz.cast(parsed);
|
return clazz.cast(parsed);
|
||||||
|
} finally {
|
||||||
|
// Automatically close the tokener to release resources back to the pool
|
||||||
|
try {
|
||||||
|
tokener.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Log or ignore IOException during cleanup - don't mask the original exception
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts parsing a JSON value at the current token position.
|
* Starts parsing a JSON value at the current token position.
|
||||||
*/
|
*/
|
||||||
private Object currentValue() throws JsonParserException {
|
private Object currentValue() throws JsonParserException {
|
||||||
// Only a value start token should appear when we're in the context of parsing a JSON value
|
// Only a value start token should appear when we're in the context of parsing a
|
||||||
|
// JSON value
|
||||||
if (token >= JsonTokener.TOKEN_VALUE_MIN)
|
if (token >= JsonTokener.TOKEN_VALUE_MIN)
|
||||||
return value;
|
return value;
|
||||||
throw tokener.createParseException(null, "Expected JSON value, got " + token, true);
|
throw tokener.createParseException(null, "Expected JSON value, got " + token, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes a token, first eating up any whitespace ahead of it. Note that number tokens are not necessarily valid
|
* Consumes a token, first eating up any whitespace ahead of it. Note that
|
||||||
|
* number tokens are not necessarily valid
|
||||||
* numbers.
|
* numbers.
|
||||||
*/
|
*/
|
||||||
private int advanceToken(boolean allowSemiString, boolean old) throws JsonParserException {
|
private int advanceToken() throws JsonParserException {
|
||||||
if (old) tokener.index--;
|
token = tokener.advanceToToken();
|
||||||
token = tokener.advanceToToken(allowSemiString);
|
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case JsonTokener.TOKEN_ARRAY_START: // Inlined function to avoid additional stack
|
case JsonTokener.TOKEN_ARRAY_START: // Inlined function to avoid additional stack
|
||||||
JsonArray list = new JsonArray();
|
JsonArray list = new JsonArray();
|
||||||
if (advanceToken(false, false) != JsonTokener.TOKEN_ARRAY_END)
|
if (advanceToken() != JsonTokener.TOKEN_ARRAY_END)
|
||||||
while (true) {
|
while (true) {
|
||||||
list.add(currentValue());
|
list.add(currentValue());
|
||||||
if (token == JsonTokener.TOKEN_SEMI_STRING)
|
if (advanceToken() == JsonTokener.TOKEN_ARRAY_END)
|
||||||
throw tokener.createParseException(null, "Semi-string is not allowed in array", true);
|
|
||||||
if (advanceToken(false, false) == JsonTokener.TOKEN_ARRAY_END)
|
|
||||||
break;
|
break;
|
||||||
if (token != JsonTokener.TOKEN_COMMA)
|
if (token != JsonTokener.TOKEN_COMMA)
|
||||||
throw tokener.createParseException(null,
|
throw tokener.createParseException(null,
|
||||||
"Expected a comma or end of the array instead of " + token, true);
|
"Expected a comma or end of the array instead of " + token, true);
|
||||||
if (advanceToken(false, false) == JsonTokener.TOKEN_ARRAY_END)
|
if (advanceToken() == JsonTokener.TOKEN_ARRAY_END)
|
||||||
throw tokener.createParseException(null, "Trailing comma found in array", true);
|
throw tokener.createParseException(null, "Trailing comma found in array", true);
|
||||||
}
|
}
|
||||||
value = list;
|
value = list;
|
||||||
return token = JsonTokener.TOKEN_ARRAY_START;
|
return token = JsonTokener.TOKEN_ARRAY_START;
|
||||||
case JsonTokener.TOKEN_OBJECT_START: // Inlined function to avoid additional stack
|
case JsonTokener.TOKEN_OBJECT_START: // Inlined function to avoid additional stack
|
||||||
JsonObject map = new JsonObject();
|
JsonObject map = new JsonObject();
|
||||||
if (advanceToken(true, false) != JsonTokener.TOKEN_OBJECT_END)
|
if (advanceToken() != JsonTokener.TOKEN_OBJECT_END)
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (token) {
|
if (token != JsonTokener.TOKEN_STRING)
|
||||||
case JsonTokener.TOKEN_NULL:
|
|
||||||
case JsonTokener.TOKEN_TRUE:
|
|
||||||
case JsonTokener.TOKEN_FALSE:
|
|
||||||
value = value.toString();
|
|
||||||
break;
|
|
||||||
case JsonTokener.TOKEN_STRING:
|
|
||||||
case JsonTokener.TOKEN_SEMI_STRING:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw tokener.createParseException(null, "Expected STRING, got " + token, true);
|
throw tokener.createParseException(null, "Expected STRING, got " + token, true);
|
||||||
}
|
String key = lazyStrings ? value.toString() : (String) value;
|
||||||
String key = (String)value;
|
if (advanceToken() != JsonTokener.TOKEN_COLON)
|
||||||
if (token == JsonTokener.TOKEN_SEMI_STRING) {
|
|
||||||
if (advanceToken(false, true) != JsonTokener.TOKEN_COLON)
|
|
||||||
throw tokener.createParseException(null, "Expected COLON, got " + token, true);
|
throw tokener.createParseException(null, "Expected COLON, got " + token, true);
|
||||||
} else if (advanceToken(false, false) != JsonTokener.TOKEN_COLON)
|
advanceToken();
|
||||||
throw tokener.createParseException(null, "Expected COLON, got " + token, true);
|
|
||||||
advanceToken(false, false);
|
|
||||||
map.put(key, currentValue());
|
map.put(key, currentValue());
|
||||||
if (advanceToken(false, false) == JsonTokener.TOKEN_OBJECT_END)
|
if (advanceToken() == JsonTokener.TOKEN_OBJECT_END)
|
||||||
break;
|
break;
|
||||||
if (token != JsonTokener.TOKEN_COMMA)
|
if (token != JsonTokener.TOKEN_COMMA)
|
||||||
throw tokener.createParseException(null,
|
throw tokener.createParseException(null,
|
||||||
"Expected a comma or end of the object instead of " + token, true);
|
"Expected a comma or end of the object instead of " + token, true);
|
||||||
if (advanceToken(true, false) == JsonTokener.TOKEN_OBJECT_END)
|
if (advanceToken() == JsonTokener.TOKEN_OBJECT_END)
|
||||||
throw tokener.createParseException(null, "Trailing object found in array", true);
|
throw tokener.createParseException(null, "Trailing object found in array", true);
|
||||||
}
|
}
|
||||||
value = map;
|
value = map;
|
||||||
@@ -234,12 +245,15 @@ public final class JsonParser {
|
|||||||
value = null;
|
value = null;
|
||||||
break;
|
break;
|
||||||
case JsonTokener.TOKEN_STRING:
|
case JsonTokener.TOKEN_STRING:
|
||||||
case JsonTokener.TOKEN_SEMI_STRING:
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
value = tokener.reusableBuffer.toString();
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
value = lazyStrings ? new LazyString(chars) : new String(chars);
|
||||||
break;
|
break;
|
||||||
case JsonTokener.TOKEN_NUMBER:
|
case JsonTokener.TOKEN_NUMBER:
|
||||||
if (lazyNumbers) {
|
if (lazyNumbers) {
|
||||||
value = new JsonLazyNumber(tokener.reusableBuffer.toString(), tokener.isDouble);
|
chars = tokener.reusableBuffer.array();
|
||||||
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
value = new JsonLazyNumber(chars, tokener.isDouble);
|
||||||
} else {
|
} else {
|
||||||
value = parseNumber();
|
value = parseNumber();
|
||||||
}
|
}
|
||||||
@@ -251,31 +265,33 @@ public final class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Number parseNumber() throws JsonParserException {
|
private Number parseNumber() throws JsonParserException {
|
||||||
String number = tokener.reusableBuffer.toString();
|
char[] number = tokener.reusableBuffer.array();
|
||||||
|
number = Arrays.copyOf(number, tokener.reusableBuffer.position());
|
||||||
|
int numLength = number.length;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (tokener.isDouble)
|
if (tokener.isDouble)
|
||||||
return Double.parseDouble(number);
|
return JavaDoubleParser.parseDouble(number);
|
||||||
|
|
||||||
// Quick parse for single-digits
|
// Quick parse for single-digits
|
||||||
if (number.length() == 1) {
|
if (numLength == 1) {
|
||||||
return number.charAt(0) - '0';
|
return number[0] - '0';
|
||||||
} else if (number.length() == 2 && number.charAt(0) == '-') {
|
} else if (numLength == 2 && number[0] == '-') {
|
||||||
return '0' - number.charAt(1);
|
return '0' - number[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: Attempt to parse using the approximate best type for this
|
// HACK: Attempt to parse using the approximate best type for this
|
||||||
boolean firstMinus = number.charAt(0) == '-';
|
boolean firstMinus = number[0] == '-';
|
||||||
int length = firstMinus ? number.length() - 1 : number.length();
|
int length = firstMinus ? numLength - 1 : numLength;
|
||||||
// CHECKSTYLE_OFF: MagicNumber
|
// CHECKSTYLE_OFF: MagicNumber
|
||||||
if (length < 10 || (length == 10 && number.charAt(firstMinus ? 1 : 0) < '2')) // 2 147 483 647
|
if (length < 10 || (length == 10 && number[firstMinus ? 1 : 0] < '2')) // 2 147 483 647
|
||||||
return Integer.parseInt(number);
|
return Integer.parseInt(new String(number));
|
||||||
if (length < 19 || (length == 19 && number.charAt(firstMinus ? 1 : 0) < '9')) // 9 223 372 036 854 775 807
|
if (length < 19 || (length == 19 && number[firstMinus ? 1 : 0] < '9')) // 9 223 372 036 854 775 807
|
||||||
return Long.parseLong(number);
|
return Long.parseLong(new String(number));
|
||||||
// CHECKSTYLE_ON: MagicNumber
|
// CHECKSTYLE_ON: MagicNumber
|
||||||
return new BigInteger(number);
|
return JavaBigIntegerParser.parseBigInteger(number);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw tokener.createParseException(e, "Malformed number: " + number, true);
|
throw tokener.createParseException(e, "Malformed number: " + new String(number), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,22 +15,31 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
|
import ch.randelshofer.fastdoubleparser.JavaDoubleParser;
|
||||||
|
import ch.randelshofer.fastdoubleparser.JavaFloatParser;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Streaming reader for JSON documents.
|
* Streaming reader for JSON documents.
|
||||||
*/
|
*/
|
||||||
public final class JsonReader {
|
public final class JsonReader implements Closeable {
|
||||||
private JsonTokener tokener;
|
private JsonTokener tokener;
|
||||||
private int token;
|
private int token;
|
||||||
private BitSet states = new BitSet();
|
private BitSet states = new BitSet();
|
||||||
private int stateIndex = 0;
|
private int stateIndex = 0;
|
||||||
private boolean inObject;
|
private boolean inObject;
|
||||||
private boolean first = true;
|
private boolean first = true;
|
||||||
private StringBuilder key = new StringBuilder();
|
// CHECKSTYLE_OFF: MagicNumber
|
||||||
|
private CharBuffer key = CharBufferPool.get(1024);
|
||||||
|
// CHECKSTYLE_ON: MagicNumber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of value that the {@link JsonReader} is positioned over.
|
* The type of value that the {@link JsonReader} is positioned over.
|
||||||
@@ -76,12 +85,19 @@ public final class JsonReader {
|
|||||||
return new JsonReader(new JsonTokener(new StringReader(s)));
|
return new JsonReader(new JsonTokener(new StringReader(s)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link JsonReader} from a {@link Reader}.
|
||||||
|
*/
|
||||||
|
public static JsonReader from(Reader reader) throws JsonParserException {
|
||||||
|
return new JsonReader(new JsonTokener(reader));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal constructor.
|
* Internal constructor.
|
||||||
*/
|
*/
|
||||||
JsonReader(JsonTokener tokener) throws JsonParserException {
|
JsonReader(JsonTokener tokener) throws JsonParserException {
|
||||||
this.tokener = tokener;
|
this.tokener = tokener;
|
||||||
token = tokener.advanceToToken(false);
|
token = tokener.advanceToToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +106,8 @@ public final class JsonReader {
|
|||||||
*/
|
*/
|
||||||
public boolean pop() throws JsonParserException {
|
public boolean pop() throws JsonParserException {
|
||||||
// CHECKSTYLE_OFF: EmptyStatement
|
// CHECKSTYLE_OFF: EmptyStatement
|
||||||
while (!next());
|
while (!next())
|
||||||
|
;
|
||||||
// CHECKSTYLE_ON: EmptyStatement
|
// CHECKSTYLE_ON: EmptyStatement
|
||||||
first = false;
|
first = false;
|
||||||
inObject = states.get(--stateIndex);
|
inObject = states.get(--stateIndex);
|
||||||
@@ -134,12 +151,15 @@ public final class JsonReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the key for the object at the current value. Does not advance to the next value.
|
* Reads the key for the object at the current value. Does not advance to the
|
||||||
|
* next value.
|
||||||
*/
|
*/
|
||||||
public String key() throws JsonParserException {
|
public String key() throws JsonParserException {
|
||||||
if (!inObject)
|
if (!inObject)
|
||||||
throw tokener.createParseException(null, "Not reading an object", true);
|
throw tokener.createParseException(null, "Not reading an object", true);
|
||||||
return key.toString();
|
char[] chars = key.array();
|
||||||
|
chars = Arrays.copyOf(chars, key.position());
|
||||||
|
return new String(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,7 +189,8 @@ public final class JsonReader {
|
|||||||
case JsonTokener.TOKEN_STRING:
|
case JsonTokener.TOKEN_STRING:
|
||||||
return string();
|
return string();
|
||||||
default:
|
default:
|
||||||
throw createTokenMismatchException(JsonTokener.TOKEN_NULL, JsonTokener.TOKEN_TRUE, JsonTokener.TOKEN_FALSE,
|
throw createTokenMismatchException(JsonTokener.TOKEN_NULL, JsonTokener.TOKEN_TRUE,
|
||||||
|
JsonTokener.TOKEN_FALSE,
|
||||||
JsonTokener.TOKEN_NUMBER, JsonTokener.TOKEN_STRING);
|
JsonTokener.TOKEN_NUMBER, JsonTokener.TOKEN_STRING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +211,9 @@ public final class JsonReader {
|
|||||||
return null;
|
return null;
|
||||||
if (token != JsonTokener.TOKEN_STRING)
|
if (token != JsonTokener.TOKEN_STRING)
|
||||||
throw createTokenMismatchException(JsonTokener.TOKEN_NULL, JsonTokener.TOKEN_STRING);
|
throw createTokenMismatchException(JsonTokener.TOKEN_NULL, JsonTokener.TOKEN_STRING);
|
||||||
return tokener.reusableBuffer.toString();
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
return new String(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,39 +234,45 @@ public final class JsonReader {
|
|||||||
public Number number() throws JsonParserException {
|
public Number number() throws JsonParserException {
|
||||||
if (token == JsonTokener.TOKEN_NULL)
|
if (token == JsonTokener.TOKEN_NULL)
|
||||||
return null;
|
return null;
|
||||||
return new JsonLazyNumber(tokener.reusableBuffer.toString(), tokener.isDouble);
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
return new JsonLazyNumber(chars, tokener.isDouble);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current value as a long.
|
* Parses the current value as a long.
|
||||||
*/
|
*/
|
||||||
public long longVal() throws JsonParserException {
|
public long longVal() throws JsonParserException {
|
||||||
String s = tokener.reusableBuffer.toString();
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
return tokener.isDouble ? (long)Double.parseDouble(s) : Long.parseLong(s);
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
return tokener.isDouble ? (long) JavaDoubleParser.parseDouble(chars) : Long.parseLong(new String(chars));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current value as an integer.
|
* Parses the current value as an integer.
|
||||||
*/
|
*/
|
||||||
public int intVal() throws JsonParserException {
|
public int intVal() throws JsonParserException {
|
||||||
String s = tokener.reusableBuffer.toString();
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
return tokener.isDouble ? (int)Double.parseDouble(s) : Integer.parseInt(s);
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
return tokener.isDouble ? (int) JavaDoubleParser.parseDouble(chars) : Integer.parseInt(new String(chars));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current value as a float.
|
* Parses the current value as a float.
|
||||||
*/
|
*/
|
||||||
public float floatVal() throws JsonParserException {
|
public float floatVal() throws JsonParserException {
|
||||||
String s = tokener.reusableBuffer.toString();
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
return Float.parseFloat(s);
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
return JavaFloatParser.parseFloat(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the current value as a double.
|
* Parses the current value as a double.
|
||||||
*/
|
*/
|
||||||
public double doubleVal() throws JsonParserException {
|
public double doubleVal() throws JsonParserException {
|
||||||
String s = tokener.reusableBuffer.toString();
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
return Double.parseDouble(s);
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
return JavaDoubleParser.parseDouble(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,7 +288,7 @@ public final class JsonReader {
|
|||||||
throw tokener.createParseException(null, "Unabled to call next() at the root", true);
|
throw tokener.createParseException(null, "Unabled to call next() at the root", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
token = tokener.advanceToToken(false);
|
token = tokener.advanceToToken();
|
||||||
|
|
||||||
if (inObject) {
|
if (inObject) {
|
||||||
if (token == JsonTokener.TOKEN_OBJECT_END) {
|
if (token == JsonTokener.TOKEN_OBJECT_END) {
|
||||||
@@ -271,16 +300,18 @@ public final class JsonReader {
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
if (token != JsonTokener.TOKEN_COMMA)
|
if (token != JsonTokener.TOKEN_COMMA)
|
||||||
throw createTokenMismatchException(JsonTokener.TOKEN_COMMA, JsonTokener.TOKEN_OBJECT_END);
|
throw createTokenMismatchException(JsonTokener.TOKEN_COMMA, JsonTokener.TOKEN_OBJECT_END);
|
||||||
token = tokener.advanceToToken(false);
|
token = tokener.advanceToToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token != JsonTokener.TOKEN_STRING)
|
if (token != JsonTokener.TOKEN_STRING)
|
||||||
throw createTokenMismatchException(JsonTokener.TOKEN_STRING);
|
throw createTokenMismatchException(JsonTokener.TOKEN_STRING);
|
||||||
key.setLength(0);
|
key.clear();
|
||||||
key.append(tokener.reusableBuffer); // reduce string garbage
|
char[] chars = tokener.reusableBuffer.array();
|
||||||
if ((token = tokener.advanceToToken(false)) != JsonTokener.TOKEN_COLON)
|
chars = Arrays.copyOf(chars, tokener.reusableBuffer.position());
|
||||||
|
key.put(chars);
|
||||||
|
if ((token = tokener.advanceToToken()) != JsonTokener.TOKEN_COLON)
|
||||||
throw createTokenMismatchException(JsonTokener.TOKEN_COLON);
|
throw createTokenMismatchException(JsonTokener.TOKEN_COLON);
|
||||||
token = tokener.advanceToToken(false);
|
token = tokener.advanceToToken();
|
||||||
} else {
|
} else {
|
||||||
if (token == JsonTokener.TOKEN_ARRAY_END) {
|
if (token == JsonTokener.TOKEN_ARRAY_END) {
|
||||||
inObject = states.get(--stateIndex);
|
inObject = states.get(--stateIndex);
|
||||||
@@ -290,7 +321,7 @@ public final class JsonReader {
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
if (token != JsonTokener.TOKEN_COMMA)
|
if (token != JsonTokener.TOKEN_COMMA)
|
||||||
throw createTokenMismatchException(JsonTokener.TOKEN_COMMA, JsonTokener.TOKEN_ARRAY_END);
|
throw createTokenMismatchException(JsonTokener.TOKEN_COMMA, JsonTokener.TOKEN_ARRAY_END);
|
||||||
token = tokener.advanceToToken(false);
|
token = tokener.advanceToToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +338,21 @@ public final class JsonReader {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases resources used by this JsonReader. Should be called when done
|
||||||
|
* reading.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (key != null) {
|
||||||
|
CharBufferPool.release(key);
|
||||||
|
key = null;
|
||||||
|
}
|
||||||
|
if (tokener != null) {
|
||||||
|
tokener.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private JsonParserException createTokenMismatchException(int... t) {
|
private JsonParserException createTokenMismatchException(int... t) {
|
||||||
return tokener.createParseException(null, "token mismatch (expected " + Arrays.toString(t)
|
return tokener.createParseException(null, "token mismatch (expected " + Arrays.toString(t)
|
||||||
+ ", was " + token + ")",
|
+ ", was " + token + ")",
|
||||||
|
@@ -159,4 +159,9 @@ public interface JsonSink<SELF extends JsonSink<SELF>> {
|
|||||||
* Ends the current array or object.
|
* Ends the current array or object.
|
||||||
*/
|
*/
|
||||||
SELF end();
|
SELF end();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the key of a key/value pair.
|
||||||
|
*/
|
||||||
|
SELF key(String key);
|
||||||
}
|
}
|
||||||
|
@@ -17,20 +17,25 @@ package com.grack.nanojson;
|
|||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal class for tokenizing JSON. Used by both {@link JsonParser} and {@link JsonReader}.
|
* Internal class for tokenizing JSON. Used by both {@link JsonParser} and
|
||||||
|
* {@link JsonReader}.
|
||||||
*/
|
*/
|
||||||
final class JsonTokener {
|
final class JsonTokener implements Closeable {
|
||||||
// Used by tests
|
// Used by tests
|
||||||
static final int BUFFER_SIZE = 32 * 1024;
|
static final int BUFFER_SIZE = 32 * 1024;
|
||||||
|
|
||||||
|
static final int MAX_CHAR_BUFFER_SIZE = 512;
|
||||||
|
|
||||||
static final int BUFFER_ROOM = 256;
|
static final int BUFFER_ROOM = 256;
|
||||||
static final int MAX_ESCAPE = 5; // uXXXX (don't need the leading slash)
|
static final int MAX_ESCAPE = 5; // uXXXX (don't need the leading slash)
|
||||||
|
|
||||||
@@ -38,14 +43,14 @@ final class JsonTokener {
|
|||||||
private int tokenCharPos, tokenCharOffset;
|
private int tokenCharPos, tokenCharOffset;
|
||||||
|
|
||||||
private boolean eof;
|
private boolean eof;
|
||||||
protected int index;
|
private int index;
|
||||||
private final Reader reader;
|
private final Reader reader;
|
||||||
private final char[] buffer = new char[BUFFER_SIZE];
|
private final char[] buffer = new char[BUFFER_SIZE];
|
||||||
private int bufferLength;
|
private int bufferLength;
|
||||||
|
|
||||||
private final boolean utf8;
|
private final boolean utf8;
|
||||||
|
|
||||||
protected StringBuilder reusableBuffer = new StringBuilder();
|
protected CharBuffer reusableBuffer = CharBufferPool.get(MAX_CHAR_BUFFER_SIZE);
|
||||||
protected boolean isDouble;
|
protected boolean isDouble;
|
||||||
|
|
||||||
static final char[] TRUE = { 'r', 'u', 'e' };
|
static final char[] TRUE = { 'r', 'u', 'e' };
|
||||||
@@ -64,11 +69,11 @@ final class JsonTokener {
|
|||||||
static final int TOKEN_NUMBER = 9;
|
static final int TOKEN_NUMBER = 9;
|
||||||
static final int TOKEN_OBJECT_START = 10;
|
static final int TOKEN_OBJECT_START = 10;
|
||||||
static final int TOKEN_ARRAY_START = 11;
|
static final int TOKEN_ARRAY_START = 11;
|
||||||
static final int TOKEN_SEMI_STRING = 12;
|
|
||||||
static final int TOKEN_VALUE_MIN = TOKEN_NULL;
|
static final int TOKEN_VALUE_MIN = TOKEN_NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Reader} that reads a UTF8 stream without decoding it for performance.
|
* A {@link Reader} that reads a UTF8 stream without decoding it for
|
||||||
|
* performance.
|
||||||
*/
|
*/
|
||||||
private static final class PseudoUtf8Reader extends Reader {
|
private static final class PseudoUtf8Reader extends Reader {
|
||||||
private final InputStream buffered;
|
private final InputStream buffered;
|
||||||
@@ -82,7 +87,7 @@ final class JsonTokener {
|
|||||||
public int read(char[] cbuf, int off, int len) throws IOException {
|
public int read(char[] cbuf, int off, int len) throws IOException {
|
||||||
int r = buffered.read(buf, off, len);
|
int r = buffered.read(buf, off, len);
|
||||||
for (int i = off; i < off + r; i++)
|
for (int i = off; i < off + r; i++)
|
||||||
cbuf[i] = (char)buf[i];
|
cbuf[i] = (char) buf[i];
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,30 +181,17 @@ final class JsonTokener {
|
|||||||
|
|
||||||
fixupAfterRawBufferRead();
|
fixupAfterRawBufferRead();
|
||||||
|
|
||||||
// The token shouldn't end with something other than an ASCII letter
|
// The token should end with something other than an ASCII letter
|
||||||
switch (peekChar()) {
|
if (isAsciiLetter(peekChar()))
|
||||||
case ',':
|
|
||||||
case ':':
|
|
||||||
case '{':
|
|
||||||
case '}':
|
|
||||||
case '[':
|
|
||||||
case ']':
|
|
||||||
case ' ':
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
case '\t':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw createHelpfulException(first, expected, expected.length);
|
throw createHelpfulException(first, expected, expected.length);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Steps through to the end of the current number token (a non-digit token).
|
* Steps through to the end of the current number token (a non-digit token).
|
||||||
*/
|
*/
|
||||||
void consumeTokenNumber(char savedChar) throws JsonParserException {
|
void consumeTokenNumber(char savedChar) throws JsonParserException {
|
||||||
reusableBuffer.setLength(0);
|
reusableBuffer.clear();
|
||||||
reusableBuffer.append(savedChar);
|
reusableBuffer.put(savedChar);
|
||||||
isDouble = false;
|
isDouble = false;
|
||||||
|
|
||||||
// The JSON spec is way stricter about number formats than
|
// The JSON spec is way stricter about number formats than
|
||||||
@@ -214,8 +206,7 @@ final class JsonTokener {
|
|||||||
state = 2;
|
state = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
outer:
|
outer: while (true) {
|
||||||
while (true) {
|
|
||||||
int n = ensureBuffer(BUFFER_ROOM);
|
int n = ensureBuffer(BUFFER_ROOM);
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
break outer;
|
break outer;
|
||||||
@@ -226,58 +217,67 @@ final class JsonTokener {
|
|||||||
break outer;
|
break outer;
|
||||||
|
|
||||||
int ns = -1;
|
int ns = -1;
|
||||||
sw:
|
sw: switch (state) {
|
||||||
switch (state) {
|
|
||||||
case 1: // start leading negative
|
case 1: // start leading negative
|
||||||
if (nc == '0') {
|
if (nc == '0') {
|
||||||
ns = 3; break sw;
|
ns = 3;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
if (nc > '0' && nc <= '9') {
|
if (nc > '0' && nc <= '9') {
|
||||||
ns = 2; break sw;
|
ns = 2;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2: // no leading zero
|
case 2: // no leading zero
|
||||||
case 3: // leading zero
|
case 3: // leading zero
|
||||||
if ((nc >= '0' && nc <= '9') && state == 2) {
|
if ((nc >= '0' && nc <= '9') && state == 2) {
|
||||||
ns = 2; break sw;
|
ns = 2;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
if (nc == '.') {
|
if (nc == '.') {
|
||||||
isDouble = true;
|
isDouble = true;
|
||||||
ns = 4; break sw;
|
ns = 4;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
if (nc == 'e' || nc == 'E') {
|
if (nc == 'e' || nc == 'E') {
|
||||||
isDouble = true;
|
isDouble = true;
|
||||||
ns = 6; break sw;
|
ns = 6;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 4: // after period
|
case 4: // after period
|
||||||
case 5: // after period, one digit read
|
case 5: // after period, one digit read
|
||||||
if (nc >= '0' && nc <= '9') {
|
if (nc >= '0' && nc <= '9') {
|
||||||
ns = 5; break sw;
|
ns = 5;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
if ((nc == 'e' || nc == 'E') && state == 5) {
|
if ((nc == 'e' || nc == 'E') && state == 5) {
|
||||||
isDouble = true;
|
isDouble = true;
|
||||||
ns = 6; break sw;
|
ns = 6;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 6: // after exponent
|
case 6: // after exponent
|
||||||
case 7: // after exponent and sign
|
case 7: // after exponent and sign
|
||||||
if (nc == '+' || nc == '-' && state == 6) {
|
if (nc == '+' || nc == '-' && state == 6) {
|
||||||
ns = 7; break sw;
|
ns = 7;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
if (nc >= '0' && nc <= '9') {
|
if (nc >= '0' && nc <= '9') {
|
||||||
ns = 8; break sw;
|
ns = 8;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 8: // after digits
|
case 8: // after digits
|
||||||
if (nc >= '0' && nc <= '9') {
|
if (nc >= '0' && nc <= '9') {
|
||||||
ns = 8; break sw;
|
ns = 8;
|
||||||
|
break sw;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert false : "Impossible"; // will throw malformed number
|
assert false : "Impossible"; // will throw malformed number
|
||||||
}
|
}
|
||||||
reusableBuffer.append(nc);
|
reusableBuffer.put(nc);
|
||||||
index++;
|
index++;
|
||||||
if (ns == -1)
|
if (ns == -1)
|
||||||
throw createParseException(null, "Malformed number: " + reusableBuffer, true);
|
throw createParseException(null, "Malformed number: " + reusableBuffer, true);
|
||||||
@@ -296,44 +296,50 @@ final class JsonTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Steps through to the end of the current string token (the unescaped double quote).
|
* Steps through to the end of the current string token (the unescaped double
|
||||||
|
* quote).
|
||||||
*/
|
*/
|
||||||
void consumeTokenString(int cc) throws JsonParserException {
|
void consumeTokenString() throws JsonParserException {
|
||||||
reusableBuffer.setLength(0);
|
reusableBuffer.position(0);
|
||||||
|
|
||||||
// Assume no escapes or UTF-8 in the string to start (fast path)
|
// Assume no escapes or UTF-8 in the string to start (fast path)
|
||||||
start:
|
start: while (true) {
|
||||||
while (true) {
|
|
||||||
int n = ensureBuffer(BUFFER_ROOM);
|
int n = ensureBuffer(BUFFER_ROOM);
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
throw createParseException(null, "String was not terminated before end of input", true);
|
throw createParseException(null, "String was not terminated before end of input", true);
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
char c = stringChar();
|
char c = stringChar();
|
||||||
if (c == cc) {
|
if (c == '"') {
|
||||||
// Use the index before we fixup
|
// Use the index before we fixup
|
||||||
reusableBuffer.append(buffer, index - i - 1, i);
|
expandBufferIfNeeded(i);
|
||||||
|
reusableBuffer.put(buffer, index - i - 1, i);
|
||||||
fixupAfterRawBufferRead();
|
fixupAfterRawBufferRead();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (c == '\\' || (utf8 && (c & 0x80) != 0)) {
|
if (c == '\\' || (utf8 && (c & 0x80) != 0)) {
|
||||||
reusableBuffer.append(buffer, index - i - 1, i);
|
expandBufferIfNeeded(i);
|
||||||
|
reusableBuffer.put(buffer, index - i - 1, i);
|
||||||
index--;
|
index--;
|
||||||
break start;
|
break start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reusableBuffer.append(buffer, index - n, n);
|
expandBufferIfNeeded(n);
|
||||||
|
reusableBuffer.put(buffer, index - n, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
outer:
|
outer: while (true) {
|
||||||
while (true) {
|
|
||||||
int n = ensureBuffer(BUFFER_ROOM);
|
int n = ensureBuffer(BUFFER_ROOM);
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
throw createParseException(null, "String was not terminated before end of input", true);
|
throw createParseException(null, "String was not terminated before end of input", true);
|
||||||
|
|
||||||
int end = index + n;
|
int end = index + n;
|
||||||
while (index < end) {
|
while (index < end) {
|
||||||
|
// Ensure at least 1 char of space for upcoming output (common case). Escapes
|
||||||
|
// and
|
||||||
|
// UTF-8 multi-byte sequences will further ensure space as needed.
|
||||||
|
expandBufferIfNeeded(1);
|
||||||
char c = stringChar();
|
char c = stringChar();
|
||||||
|
|
||||||
if (utf8 && (c & 0x80) != 0) {
|
if (utf8 && (c & 0x80) != 0) {
|
||||||
@@ -343,15 +349,9 @@ final class JsonTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"':
|
case '\"':
|
||||||
case '\'':
|
|
||||||
if (c == cc) {
|
|
||||||
fixupAfterRawBufferRead();
|
fixupAfterRawBufferRead();
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
reusableBuffer.append(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '\\':
|
case '\\':
|
||||||
// Ensure that we have at least MAX_ESCAPE here in the buffer
|
// Ensure that we have at least MAX_ESCAPE here in the buffer
|
||||||
if (end - index < MAX_ESCAPE) {
|
if (end - index < MAX_ESCAPE) {
|
||||||
@@ -361,31 +361,32 @@ final class JsonTokener {
|
|||||||
// Make sure that there's enough chars for a \\uXXXX escape
|
// Make sure that there's enough chars for a \\uXXXX escape
|
||||||
if (buffer[index] == 'u' && n < MAX_ESCAPE) {
|
if (buffer[index] == 'u' && n < MAX_ESCAPE) {
|
||||||
index = bufferLength; // Reset index to last valid location
|
index = bufferLength; // Reset index to last valid location
|
||||||
throw createParseException(null, "EOF encountered in the middle of a string escape", false);
|
throw createParseException(null,
|
||||||
|
"EOF encountered in the middle of a string escape",
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char escape = buffer[index++];
|
char escape = buffer[index++];
|
||||||
switch (escape) {
|
switch (escape) {
|
||||||
case 'b':
|
case 'b':
|
||||||
reusableBuffer.append('\b');
|
reusableBuffer.put('\b');
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
reusableBuffer.append('\f');
|
reusableBuffer.put('\f');
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
reusableBuffer.append('\n');
|
reusableBuffer.put('\n');
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
reusableBuffer.append('\r');
|
reusableBuffer.put('\r');
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
reusableBuffer.append('\t');
|
reusableBuffer.put('\t');
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '"':
|
||||||
case '\'':
|
|
||||||
case '/':
|
case '/':
|
||||||
case '\\':
|
case '\\':
|
||||||
reusableBuffer.append(escape);
|
reusableBuffer.put(escape);
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
int escaped = 0;
|
int escaped = 0;
|
||||||
@@ -400,155 +401,28 @@ final class JsonTokener {
|
|||||||
} else if (digit >= 'a' && digit <= 'f') {
|
} else if (digit >= 'a' && digit <= 'f') {
|
||||||
escaped |= (digit - 'a') + 10;
|
escaped |= (digit - 'a') + 10;
|
||||||
} else {
|
} else {
|
||||||
throw createParseException(null, "Expected unicode hex escape character: "
|
throw createParseException(null,
|
||||||
+ (char)digit + " (" + digit + ")", false);
|
"Expected unicode hex escape character: "
|
||||||
|
+ (char) digit + " (" + digit + ")", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reusableBuffer.append((char)escaped);
|
reusableBuffer.put((char) escaped);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw createParseException(null, "Invalid escape: \\" + escape, false);
|
throw createParseException(null, "Invalid escape: \\" + escape, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
reusableBuffer.append(c);
|
reusableBuffer.put(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > bufferLength) {
|
if (index > bufferLength) {
|
||||||
index = bufferLength; // Reset index to last valid location
|
index = bufferLength; // Reset index to last valid location
|
||||||
throw createParseException(null, "EOF encountered in the middle of a string escape", false);
|
throw createParseException(null,
|
||||||
}
|
"EOF encountered in the middle of a string escape",
|
||||||
}
|
false);
|
||||||
}
|
|
||||||
|
|
||||||
void consumeTokenSemiString() throws JsonParserException {
|
|
||||||
reusableBuffer.setLength(0);
|
|
||||||
|
|
||||||
start:
|
|
||||||
while (true) {
|
|
||||||
int n = ensureBuffer(BUFFER_ROOM);
|
|
||||||
if (n == 0)
|
|
||||||
throw createParseException(null, "String was not terminated before end of input", true);
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
char c = stringChar();
|
|
||||||
if (isWhitespace(c) || c == ':') {
|
|
||||||
// Use the index before we fixup
|
|
||||||
reusableBuffer.append(buffer, index - i - 1, i);
|
|
||||||
fixupAfterRawBufferRead();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (c == '\\' || (utf8 && (c & 0x80) != 0)) {
|
|
||||||
reusableBuffer.append(buffer, index - i - 1, i);
|
|
||||||
index--;
|
|
||||||
break start;
|
|
||||||
}
|
|
||||||
if (c == '[' || c == ']' || c == '{' || c == '}' || c == ',') {
|
|
||||||
throw createParseException(null, "Invalid character in semi-string: " + c, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reusableBuffer.append(buffer, index - n, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
outer:
|
|
||||||
while (true) {
|
|
||||||
int n = ensureBuffer(BUFFER_ROOM);
|
|
||||||
if (n == 0)
|
|
||||||
throw createParseException(null, "String was not terminated before end of input", true);
|
|
||||||
|
|
||||||
int end = index + n;
|
|
||||||
while (index < end) {
|
|
||||||
char c = stringChar();
|
|
||||||
|
|
||||||
if (utf8 && (c & 0x80) != 0) {
|
|
||||||
// If it's a UTF-8 codepoint, we know it won't have special meaning
|
|
||||||
consumeTokenStringUtf8Char(c);
|
|
||||||
continue outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case ' ':
|
|
||||||
case '\n':
|
|
||||||
case '\r':
|
|
||||||
case '\t':
|
|
||||||
case ':':
|
|
||||||
fixupAfterRawBufferRead();
|
|
||||||
return;
|
|
||||||
case '[':
|
|
||||||
case ']':
|
|
||||||
case '{':
|
|
||||||
case '}':
|
|
||||||
case ',':
|
|
||||||
throw createParseException(null, "Invalid character in semi-string: " + c, false);
|
|
||||||
case '\\':
|
|
||||||
// Ensure that we have at least MAX_ESCAPE here in the buffer
|
|
||||||
if (end - index < MAX_ESCAPE) {
|
|
||||||
// Re-adjust the buffer end, unlikely path
|
|
||||||
n = ensureBuffer(MAX_ESCAPE);
|
|
||||||
end = index + n;
|
|
||||||
// Make sure that there's enough chars for a \\uXXXX escape
|
|
||||||
if (buffer[index] == 'u' && n < MAX_ESCAPE) {
|
|
||||||
index = bufferLength; // Reset index to last valid location
|
|
||||||
throw createParseException(null, "EOF encountered in the middle of a string escape", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
char escape = buffer[index++];
|
|
||||||
switch (escape) {
|
|
||||||
case 'b':
|
|
||||||
reusableBuffer.append('\b');
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
reusableBuffer.append('\f');
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
reusableBuffer.append('\n');
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
reusableBuffer.append('\r');
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
reusableBuffer.append('\t');
|
|
||||||
break;
|
|
||||||
case '"':
|
|
||||||
case '/':
|
|
||||||
case '\\':
|
|
||||||
reusableBuffer.append(escape);
|
|
||||||
break;
|
|
||||||
case 'u':
|
|
||||||
int escaped = 0;
|
|
||||||
|
|
||||||
for (int j = 0; j < 4; j++) {
|
|
||||||
escaped <<= 4;
|
|
||||||
int digit = buffer[index++];
|
|
||||||
if (digit >= '0' && digit <= '9') {
|
|
||||||
escaped |= (digit - '0');
|
|
||||||
} else if (digit >= 'A' && digit <= 'F') {
|
|
||||||
escaped |= (digit - 'A') + 10;
|
|
||||||
} else if (digit >= 'a' && digit <= 'f') {
|
|
||||||
escaped |= (digit - 'a') + 10;
|
|
||||||
} else {
|
|
||||||
throw createParseException(null, "Expected unicode hex escape character: "
|
|
||||||
+ (char)digit + " (" + digit + ")", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reusableBuffer.append((char)escaped);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw createParseException(null, "Invalid escape: \\" + escape, false);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reusableBuffer.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > bufferLength) {
|
|
||||||
index = bufferLength; // Reset index to last valid location
|
|
||||||
throw createParseException(null, "EOF encountered in the middle of a string escape", false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,6 +431,9 @@ final class JsonTokener {
|
|||||||
private void consumeTokenStringUtf8Char(char c) throws JsonParserException {
|
private void consumeTokenStringUtf8Char(char c) throws JsonParserException {
|
||||||
ensureBuffer(5);
|
ensureBuffer(5);
|
||||||
|
|
||||||
|
// Worst case (supplementary plane) decodes to a surrogate pair (2 chars)
|
||||||
|
expandBufferIfNeeded(2);
|
||||||
|
|
||||||
// Hand-UTF8-decoding
|
// Hand-UTF8-decoding
|
||||||
switch (c & 0xf0) {
|
switch (c & 0xf0) {
|
||||||
case 0x80:
|
case 0x80:
|
||||||
@@ -572,18 +449,19 @@ final class JsonTokener {
|
|||||||
false);
|
false);
|
||||||
// fall-through
|
// fall-through
|
||||||
case 0xd0:
|
case 0xd0:
|
||||||
c = (char)((c & 0x1f) << 6 | (buffer[index++] & 0x3f));
|
c = (char) ((c & 0x1f) << 6 | (buffer[index++] & 0x3f));
|
||||||
reusableBuffer.append(c);
|
reusableBuffer.put(c);
|
||||||
utf8adjust++;
|
utf8adjust++;
|
||||||
break;
|
break;
|
||||||
case 0xe0:
|
case 0xe0:
|
||||||
c = (char)((c & 0x0f) << 12 | (buffer[index++] & 0x3f) << 6 | (buffer[index++] & 0x3f));
|
c = (char) ((c & 0x0f) << 12 | (buffer[index++] & 0x3f) << 6 | (buffer[index++] & 0x3f));
|
||||||
utf8adjust += 2;
|
utf8adjust += 2;
|
||||||
// Check for illegally-encoded surrogate - http://unicode.org/faq/utf_bom.html#utf8-4
|
// Check for illegally-encoded surrogate -
|
||||||
|
// http://unicode.org/faq/utf_bom.html#utf8-4
|
||||||
if ((c >= '\ud800' && c <= '\udbff') || (c >= '\udc00' && c <= '\udfff'))
|
if ((c >= '\ud800' && c <= '\udbff') || (c >= '\udc00' && c <= '\udfff'))
|
||||||
throw createParseException(null, "Illegal UTF-8 codepoint: 0x" + Integer.toHexString(c),
|
throw createParseException(null, "Illegal UTF-8 codepoint: 0x" + Integer.toHexString(c),
|
||||||
false);
|
false);
|
||||||
reusableBuffer.append(c);
|
reusableBuffer.put(c);
|
||||||
break;
|
break;
|
||||||
case 0xf0:
|
case 0xf0:
|
||||||
if ((c & 0xf) >= 5)
|
if ((c & 0xf) >= 5)
|
||||||
@@ -594,8 +472,8 @@ final class JsonTokener {
|
|||||||
switch ((c & 0xc) >> 2) {
|
switch ((c & 0xc) >> 2) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
reusableBuffer.appendCodePoint((c & 7) << 18 | (buffer[index++] & 0x3f) << 12
|
reusableBuffer.put(Character.toChars((c & 7) << 18 | (buffer[index++] & 0x3f) << 12
|
||||||
| (buffer[index++] & 0x3f) << 6 | (buffer[index++] & 0x3f));
|
| (buffer[index++] & 0x3f) << 6 | (buffer[index++] & 0x3f)));
|
||||||
utf8adjust += 3;
|
utf8adjust += 3;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
@@ -604,14 +482,16 @@ final class JsonTokener {
|
|||||||
| (buffer[index++] & 0x3f) << 6 | (buffer[index++] & 0x3f);
|
| (buffer[index++] & 0x3f) << 6 | (buffer[index++] & 0x3f);
|
||||||
throw createParseException(null,
|
throw createParseException(null,
|
||||||
"Unable to represent codepoint 0x" + Integer.toHexString(codepoint)
|
"Unable to represent codepoint 0x" + Integer.toHexString(codepoint)
|
||||||
+ " in a Java string", false);
|
+ " in a Java string",
|
||||||
|
false);
|
||||||
case 3:
|
case 3:
|
||||||
codepoint = (c & 1) << 30 | (buffer[index++] & 0x3f) << 24 | (buffer[index++] & 0x3f) << 18
|
codepoint = (c & 1) << 30 | (buffer[index++] & 0x3f) << 24 | (buffer[index++] & 0x3f) << 18
|
||||||
| (buffer[index++] & 0x3f) << 12 | (buffer[index++] & 0x3f) << 6
|
| (buffer[index++] & 0x3f) << 12 | (buffer[index++] & 0x3f) << 6
|
||||||
| (buffer[index++] & 0x3f);
|
| (buffer[index++] & 0x3f);
|
||||||
throw createParseException(null,
|
throw createParseException(null,
|
||||||
"Unable to represent codepoint 0x" + Integer.toHexString(codepoint)
|
"Unable to represent codepoint 0x" + Integer.toHexString(codepoint)
|
||||||
+ " in a Java string", false);
|
+ " in a Java string",
|
||||||
|
false);
|
||||||
default:
|
default:
|
||||||
assert false : "Impossible";
|
assert false : "Impossible";
|
||||||
}
|
}
|
||||||
@@ -625,7 +505,8 @@ final class JsonTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances a character, throwing if it is illegal in the context of a JSON string.
|
* Advances a character, throwing if it is illegal in the context of a JSON
|
||||||
|
* string.
|
||||||
*/
|
*/
|
||||||
private char stringChar() throws JsonParserException {
|
private char stringChar() throws JsonParserException {
|
||||||
char c = buffer[index++];
|
char c = buffer[index++];
|
||||||
@@ -692,7 +573,8 @@ final class JsonTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that there is enough room in the buffer to directly access the next N chars via buffer[].
|
* Ensures that there is enough room in the buffer to directly access the next N
|
||||||
|
* chars via buffer[].
|
||||||
*/
|
*/
|
||||||
int ensureBuffer(int n) throws JsonParserException {
|
int ensureBuffer(int n) throws JsonParserException {
|
||||||
// We're good here
|
// We're good here
|
||||||
@@ -700,7 +582,8 @@ final class JsonTokener {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nope, we need to read more, but we also have to retain whatever buffer we have
|
// Nope, we need to read more, but we also have to retain whatever buffer we
|
||||||
|
// have
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
charOffset += index;
|
charOffset += index;
|
||||||
bufferLength = bufferLength - index;
|
bufferLength = bufferLength - index;
|
||||||
@@ -783,10 +666,11 @@ final class JsonTokener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes a token, first eating up any whitespace ahead of it. Note that number tokens are not necessarily valid
|
* Consumes a token, first eating up any whitespace ahead of it. Note that
|
||||||
|
* number tokens are not necessarily valid
|
||||||
* numbers.
|
* numbers.
|
||||||
*/
|
*/
|
||||||
int advanceToToken(boolean allowSemiString) throws JsonParserException {
|
int advanceToToken() throws JsonParserException {
|
||||||
int c = advanceChar();
|
int c = advanceChar();
|
||||||
while (isWhitespace(c))
|
while (isWhitespace(c))
|
||||||
c = advanceChar();
|
c = advanceChar();
|
||||||
@@ -794,7 +678,6 @@ final class JsonTokener {
|
|||||||
tokenCharPos = index + charOffset - rowPos - utf8adjust;
|
tokenCharPos = index + charOffset - rowPos - utf8adjust;
|
||||||
tokenCharOffset = charOffset + index;
|
tokenCharOffset = charOffset + index;
|
||||||
|
|
||||||
int oldIndex = index;
|
|
||||||
int token;
|
int token;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case -1:
|
case -1:
|
||||||
@@ -818,44 +701,19 @@ final class JsonTokener {
|
|||||||
token = TOKEN_OBJECT_END;
|
token = TOKEN_OBJECT_END;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
try {
|
|
||||||
consumeKeyword((char) c, JsonTokener.TRUE);
|
consumeKeyword((char) c, JsonTokener.TRUE);
|
||||||
token = TOKEN_TRUE;
|
token = TOKEN_TRUE;
|
||||||
} catch (JsonParserException e) {
|
|
||||||
if (allowSemiString) {
|
|
||||||
index = oldIndex - 1;
|
|
||||||
consumeTokenSemiString();
|
|
||||||
token = TOKEN_SEMI_STRING;
|
|
||||||
} else throw e;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
try {
|
consumeKeyword((char) c, JsonTokener.FALSE);
|
||||||
consumeKeyword((char)c, JsonTokener.FALSE);
|
|
||||||
token = TOKEN_FALSE;
|
token = TOKEN_FALSE;
|
||||||
} catch (JsonParserException e) {
|
|
||||||
if (allowSemiString) {
|
|
||||||
index = oldIndex - 1;
|
|
||||||
consumeTokenSemiString();
|
|
||||||
token = TOKEN_SEMI_STRING;
|
|
||||||
} else throw e;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
try {
|
consumeKeyword((char) c, JsonTokener.NULL);
|
||||||
consumeKeyword((char)c, JsonTokener.NULL);
|
|
||||||
token = TOKEN_NULL;
|
token = TOKEN_NULL;
|
||||||
} catch (JsonParserException e) {
|
|
||||||
if (allowSemiString) {
|
|
||||||
index = oldIndex - 1;
|
|
||||||
consumeTokenSemiString();
|
|
||||||
token = TOKEN_SEMI_STRING;
|
|
||||||
} else throw e;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case '"':
|
case '\"':
|
||||||
case '\'':
|
consumeTokenString();
|
||||||
consumeTokenString(c);
|
|
||||||
token = TOKEN_STRING;
|
token = TOKEN_STRING;
|
||||||
break;
|
break;
|
||||||
case '-':
|
case '-':
|
||||||
@@ -869,27 +727,20 @@ final class JsonTokener {
|
|||||||
case '7':
|
case '7':
|
||||||
case '8':
|
case '8':
|
||||||
case '9':
|
case '9':
|
||||||
consumeTokenNumber((char)c);
|
consumeTokenNumber((char) c);
|
||||||
token = TOKEN_NUMBER;
|
token = TOKEN_NUMBER;
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
case '.':
|
case '.':
|
||||||
throw createParseException(null, "Numbers may not start with '" + (char)c + "'", true);
|
throw createParseException(null, "Numbers may not start with '" + (char) c + "'", true);
|
||||||
default:
|
default:
|
||||||
if (allowSemiString) {
|
|
||||||
index--;
|
|
||||||
consumeTokenSemiString();
|
|
||||||
token = TOKEN_SEMI_STRING;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (isAsciiLetter(c))
|
if (isAsciiLetter(c))
|
||||||
throw createHelpfulException((char)c, null, 0);
|
throw createHelpfulException((char) c, null, 0);
|
||||||
|
|
||||||
throw createParseException(null, "Unexpected character: " + (char)c, true);
|
throw createParseException(null, "Unexpected character: " + (char) c, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// consumeWhitespace();
|
// consumeWhitespace();
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -908,6 +759,18 @@ final class JsonTokener {
|
|||||||
eof = refillBuffer();
|
eof = refillBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expandBufferIfNeeded(int size) {
|
||||||
|
if (reusableBuffer.remaining() < size) {
|
||||||
|
int oldPos = reusableBuffer.position();
|
||||||
|
int increment = Math.max(512, size - reusableBuffer.remaining());
|
||||||
|
CharBuffer newBuffer = CharBuffer.allocate(reusableBuffer.capacity() + increment);
|
||||||
|
reusableBuffer.flip(); // position -> 0, limit -> oldPos
|
||||||
|
newBuffer.put(reusableBuffer); // copy all existing data
|
||||||
|
reusableBuffer = newBuffer;
|
||||||
|
reusableBuffer.position(oldPos); // restore write position at end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws a helpful exception based on the current alphanumeric token.
|
* Throws a helpful exception based on the current alphanumeric token.
|
||||||
*/
|
*/
|
||||||
@@ -919,14 +782,30 @@ final class JsonTokener {
|
|||||||
|
|
||||||
// Consume the whole pseudo-token to make a better error message
|
// Consume the whole pseudo-token to make a better error message
|
||||||
while (isAsciiLetter(peekChar()) && errorToken.length() < 15)
|
while (isAsciiLetter(peekChar()) && errorToken.length() < 15)
|
||||||
errorToken.append((char)advanceChar());
|
errorToken.append((char) advanceChar());
|
||||||
|
|
||||||
return createParseException(null, "Unexpected token '" + errorToken + "'"
|
return createParseException(null, "Unexpected token '" + errorToken + "'"
|
||||||
+ (expected == null ? "" : ". Did you mean '" + first + new String(expected) + "'?"), true);
|
+ (expected == null ? "" : ". Did you mean '" + first + new String(expected) + "'?"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@link JsonParserException} and fills it from the current line and char position.
|
* Releases resources used by this JsonTokener. Should be called when done
|
||||||
|
* tokenizing.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (reusableBuffer != null) {
|
||||||
|
CharBufferPool.release(reusableBuffer);
|
||||||
|
reusableBuffer = null;
|
||||||
|
}
|
||||||
|
if (reader != null) {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link JsonParserException} and fills it from the current line and
|
||||||
|
* char position.
|
||||||
*/
|
*/
|
||||||
JsonParserException createParseException(Exception e, String message, boolean tokenPos) {
|
JsonParserException createParseException(Exception e, String message, boolean tokenPos) {
|
||||||
if (tokenPos)
|
if (tokenPos)
|
||||||
|
@@ -151,7 +151,7 @@ public final class JsonWriter {
|
|||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
public static JsonWriterContext indent(String indent) {
|
public static JsonWriter.JsonWriterContext indent(String indent) {
|
||||||
if (indent == null) {
|
if (indent == null) {
|
||||||
throw new IllegalArgumentException("indent must be non-null");
|
throw new IllegalArgumentException("indent must be non-null");
|
||||||
}
|
}
|
||||||
|
@@ -50,6 +50,7 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
private int stateIndex = 0;
|
private int stateIndex = 0;
|
||||||
private boolean first = true;
|
private boolean first = true;
|
||||||
private boolean inObject;
|
private boolean inObject;
|
||||||
|
private String pendingKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sequence to use for indenting.
|
* Sequence to use for indenting.
|
||||||
@@ -123,7 +124,8 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
Object o = entry.getValue();
|
Object o = entry.getValue();
|
||||||
if (!(entry.getKey() instanceof String))
|
if (!(entry.getKey() instanceof String))
|
||||||
throw new JsonWriterException("Invalid key type for map: "
|
throw new JsonWriterException("Invalid key type for map: "
|
||||||
+ (entry.getKey() == null ? "null" : entry.getKey()
|
+ (entry.getKey() == null ? "null"
|
||||||
|
: entry.getKey()
|
||||||
.getClass()));
|
.getClass()));
|
||||||
String k = (String) entry.getKey();
|
String k = (String) entry.getKey();
|
||||||
value(k, o);
|
value(k, o);
|
||||||
@@ -152,6 +154,8 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
return nul();
|
return nul();
|
||||||
else if (o instanceof String)
|
else if (o instanceof String)
|
||||||
return value((String) o);
|
return value((String) o);
|
||||||
|
else if (o instanceof LazyString)
|
||||||
|
return value(o.toString());
|
||||||
else if (o instanceof Number)
|
else if (o instanceof Number)
|
||||||
return value(((Number) o));
|
return value(((Number) o));
|
||||||
else if (o instanceof Boolean)
|
else if (o instanceof Boolean)
|
||||||
@@ -166,7 +170,9 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
value(Array.get(o, i));
|
value(Array.get(o, i));
|
||||||
return end();
|
return end();
|
||||||
} else
|
} else if (o instanceof JsonConvertible)
|
||||||
|
return value(((JsonConvertible) o).toJsonValue());
|
||||||
|
else
|
||||||
throw new JsonWriterException("Unable to handle type: "
|
throw new JsonWriterException("Unable to handle type: "
|
||||||
+ o.getClass());
|
+ o.getClass());
|
||||||
}
|
}
|
||||||
@@ -177,6 +183,8 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
return nul(key);
|
return nul(key);
|
||||||
else if (o instanceof String)
|
else if (o instanceof String)
|
||||||
return value(key, (String) o);
|
return value(key, (String) o);
|
||||||
|
else if (o instanceof LazyString)
|
||||||
|
return value(key, o.toString());
|
||||||
else if (o instanceof Number)
|
else if (o instanceof Number)
|
||||||
return value(key, (Number) o);
|
return value(key, (Number) o);
|
||||||
else if (o instanceof Boolean)
|
else if (o instanceof Boolean)
|
||||||
@@ -191,7 +199,9 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
for (int i = 0; i < length; i++)
|
for (int i = 0; i < length; i++)
|
||||||
value(Array.get(o, i));
|
value(Array.get(o, i));
|
||||||
return end();
|
return end();
|
||||||
} else
|
} else if (o instanceof JsonConvertible)
|
||||||
|
return value(key, ((JsonConvertible) o).toJsonValue());
|
||||||
|
else
|
||||||
throw new JsonWriterException("Unable to handle type: "
|
throw new JsonWriterException("Unable to handle type: "
|
||||||
+ o.getClass());
|
+ o.getClass());
|
||||||
}
|
}
|
||||||
@@ -243,7 +253,7 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
@Override
|
@Override
|
||||||
public SELF value(Number n) {
|
public SELF value(Number n) {
|
||||||
preValue();
|
preValue();
|
||||||
if (n == null)
|
if (n == null || nullish(n))
|
||||||
raw(NULL);
|
raw(NULL);
|
||||||
else
|
else
|
||||||
raw(n.toString());
|
raw(n.toString());
|
||||||
@@ -372,12 +382,25 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
return castThis();
|
return castThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SELF key(String key) {
|
||||||
|
if (key == null)
|
||||||
|
throw new NullPointerException("key");
|
||||||
|
if (pendingKey != null)
|
||||||
|
throw new JsonWriterException(
|
||||||
|
"Invalid call to emit a key immediately after emitting a key");
|
||||||
|
pendingKey = key;
|
||||||
|
return castThis();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that the object is in the finished state.
|
* Ensures that the object is in the finished state.
|
||||||
*
|
*
|
||||||
* @throws JsonWriterException
|
* @throws JsonWriterException
|
||||||
* if the written JSON is not properly balanced, ie: all arrays
|
* if the written JSON is not properly balanced, ie:
|
||||||
* and objects that were started have been properly ended.
|
* all arrays
|
||||||
|
* and objects that were started have been properly
|
||||||
|
* ended.
|
||||||
*/
|
*/
|
||||||
protected void doneInternal() {
|
protected void doneInternal() {
|
||||||
if (stateIndex > 0)
|
if (stateIndex > 0)
|
||||||
@@ -434,7 +457,7 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
if (utf8) {
|
if (utf8) {
|
||||||
if (bo + 1 > BUFFER_SIZE)
|
if (bo + 1 > BUFFER_SIZE)
|
||||||
flush();
|
flush();
|
||||||
bb[bo++] = (byte)c;
|
bb[bo++] = (byte) c;
|
||||||
} else {
|
} else {
|
||||||
buffer.append(c);
|
buffer.append(c);
|
||||||
if (buffer.length() > BUFFER_SIZE) {
|
if (buffer.length() > BUFFER_SIZE) {
|
||||||
@@ -472,6 +495,12 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void preValue() {
|
private void preValue() {
|
||||||
|
if (pendingKey != null) {
|
||||||
|
String key = pendingKey;
|
||||||
|
pendingKey = null;
|
||||||
|
preValue(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (inObject)
|
if (inObject)
|
||||||
throw new JsonWriterException(
|
throw new JsonWriterException(
|
||||||
"Invalid call to emit a keyless value while writing an object");
|
"Invalid call to emit a keyless value while writing an object");
|
||||||
@@ -483,6 +512,9 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
if (!inObject)
|
if (!inObject)
|
||||||
throw new JsonWriterException(
|
throw new JsonWriterException(
|
||||||
"Invalid call to emit a key value while not writing an object");
|
"Invalid call to emit a key value while not writing an object");
|
||||||
|
if (pendingKey != null)
|
||||||
|
throw new JsonWriterException(
|
||||||
|
"Invalid call to emit a key value immediately after emitting a key");
|
||||||
|
|
||||||
pre();
|
pre();
|
||||||
|
|
||||||
@@ -547,7 +579,12 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (utf8) {
|
if (utf8) {
|
||||||
if (bo + 4 > BUFFER_SIZE) // 4 is the max char size
|
// Ensure space for the largest possible UTF-8 sequence (4 bytes) before
|
||||||
|
// encoding. Even if this char ultimately encodes to 1,2 or 3 bytes,
|
||||||
|
// reserving for 4 keeps logic simple and guarantees we never start a
|
||||||
|
// multi-byte sequence that would be split across a flush.
|
||||||
|
if (bo + 4 > BUFFER_SIZE) // 4 is the max UTF-8 byte length for a single Unicode scalar
|
||||||
|
// value
|
||||||
flush();
|
flush();
|
||||||
if (c < 0x80) {
|
if (c < 0x80) {
|
||||||
bb[bo++] = (byte) c;
|
bb[bo++] = (byte) c;
|
||||||
@@ -558,20 +595,39 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
bb[bo++] = (byte) (0xe0 | c >> 12);
|
bb[bo++] = (byte) (0xe0 | c >> 12);
|
||||||
bb[bo++] = (byte) (0x80 | (c >> 6) & 0x3f);
|
bb[bo++] = (byte) (0x80 | (c >> 6) & 0x3f);
|
||||||
bb[bo++] = (byte) (0x80 | c & 0x3f);
|
bb[bo++] = (byte) (0x80 | c & 0x3f);
|
||||||
} else if (c < 0xdfff) {
|
} else if (Character.isHighSurrogate(c)) {
|
||||||
// TODO: bad surrogates
|
// Surrogate pair handling (supplementary plane character)
|
||||||
i++;
|
// We have a high surrogate; must be followed by a low surrogate to form a valid
|
||||||
|
// code point.
|
||||||
int fc = Character.toCodePoint(c, s.charAt(i));
|
if (i + 1 >= s.length()) {
|
||||||
if (fc < 0x1fffff) {
|
throw new JsonWriterException("Invalid high surrogate at end of string");
|
||||||
bb[bo++] = (byte) (0xf0 | fc >> 18);
|
}
|
||||||
|
char lowSurrogate = s.charAt(i + 1);
|
||||||
|
if (!Character.isLowSurrogate(lowSurrogate)) {
|
||||||
|
throw new JsonWriterException("Invalid surrogate pair: "
|
||||||
|
+ "high surrogate not followed by low surrogate");
|
||||||
|
}
|
||||||
|
// Need 4 bytes for any supplementary code point in UTF-8. Flush first if
|
||||||
|
// insufficient space
|
||||||
|
// so the 4-byte sequence is never split across buffers.
|
||||||
|
if (bo + 4 > BUFFER_SIZE)
|
||||||
|
flush();
|
||||||
|
i++; // consume the low surrogate
|
||||||
|
int fc = Character.toCodePoint(c, lowSurrogate); // full scalar value
|
||||||
|
// Unicode scalar values are defined only up to U+10FFFF
|
||||||
|
// (exclusive upper bound 0x110000).
|
||||||
|
if (fc < 0x110000) {
|
||||||
|
bb[bo++] = (byte) (0xf0 | (fc >> 18));
|
||||||
bb[bo++] = (byte) (0x80 | (fc >> 12) & 0x3f);
|
bb[bo++] = (byte) (0x80 | (fc >> 12) & 0x3f);
|
||||||
bb[bo++] = (byte) (0x80 | (fc >> 6) & 0x3f);
|
bb[bo++] = (byte) (0x80 | (fc >> 6) & 0x3f);
|
||||||
bb[bo++] = (byte) (0x80 | fc & 0x3f);
|
bb[bo++] = (byte) (0x80 | fc & 0x3f);
|
||||||
} else {
|
} else {
|
||||||
throw new JsonWriterException("Unable to encode character 0x"
|
throw new JsonWriterException(
|
||||||
+ Integer.toHexString(fc));
|
"Unable to encode character 0x" + Integer.toHexString(fc));
|
||||||
}
|
}
|
||||||
|
} else if (Character.isLowSurrogate(c)) {
|
||||||
|
throw new JsonWriterException(
|
||||||
|
"Invalid low surrogate without preceding high surrogate");
|
||||||
} else {
|
} else {
|
||||||
bb[bo++] = (byte) (0xe0 | c >> 12);
|
bb[bo++] = (byte) (0xe0 | c >> 12);
|
||||||
bb[bo++] = (byte) (0x80 | (c >> 6) & 0x3f);
|
bb[bo++] = (byte) (0x80 | (c >> 6) & 0x3f);
|
||||||
@@ -594,4 +650,25 @@ class JsonWriterBase<SELF extends JsonWriterBase<SELF>> implements
|
|||||||
return c < ' ' || (c >= '\u0080' && c < '\u00a0')
|
return c < ' ' || (c >= '\u0080' && c < '\u00a0')
|
||||||
|| (c >= '\u2000' && c < '\u2100');
|
|| (c >= '\u2000' && c < '\u2100');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the number becomes null when converted to JSON. json.org spec
|
||||||
|
* does not specify
|
||||||
|
* NaN or Infinity as numbers, and modern JavaScript engines convert them to
|
||||||
|
* null.
|
||||||
|
*
|
||||||
|
* @param n a number
|
||||||
|
* @return true if the number is nullish.
|
||||||
|
*/
|
||||||
|
private boolean nullish(Number n) {
|
||||||
|
if (n instanceof Double) {
|
||||||
|
Double d = (Double) n;
|
||||||
|
return d.isNaN() || d.isInfinite();
|
||||||
|
}
|
||||||
|
if (n instanceof Float) {
|
||||||
|
Float f = (Float) n;
|
||||||
|
return f.isNaN() || f.isInfinite();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
126
src/main/java/com/grack/nanojson/LazyString.java
Normal file
126
src/main/java/com/grack/nanojson/LazyString.java
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 The nanojson Authors
|
||||||
|
*
|
||||||
|
* Licensed 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 com.grack.nanojson;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class LazyString implements CharSequence {
|
||||||
|
|
||||||
|
private final int length;
|
||||||
|
|
||||||
|
private char[] value;
|
||||||
|
private String stringValue;
|
||||||
|
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
public LazyString(char[] value) {
|
||||||
|
this.value = value;
|
||||||
|
this.length = value.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LazyString(String value) {
|
||||||
|
this.stringValue = value;
|
||||||
|
this.length = value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char charAt(int index) {
|
||||||
|
String str = stringValue; // Local ref copy to avoid race
|
||||||
|
if (str != null) {
|
||||||
|
return str.charAt(index);
|
||||||
|
}
|
||||||
|
char[] arr = value; // Local ref copy to avoid race
|
||||||
|
if (arr != null) {
|
||||||
|
return arr[index];
|
||||||
|
}
|
||||||
|
// Fallback if value was nullified
|
||||||
|
return toString().charAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence subSequence(int start, int end) {
|
||||||
|
String str = stringValue; // Local ref copy to avoid race
|
||||||
|
if (str != null) {
|
||||||
|
return str.subSequence(start, end);
|
||||||
|
}
|
||||||
|
char[] arr = value; // Local ref copy to avoid race
|
||||||
|
if (arr != null) {
|
||||||
|
return new LazyString(Arrays.copyOfRange(arr, start, end));
|
||||||
|
}
|
||||||
|
// Fallback to string-based subsequence
|
||||||
|
return toString().subSequence(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (stringValue != null) {
|
||||||
|
return stringValue;
|
||||||
|
}
|
||||||
|
synchronized (lock) {
|
||||||
|
if (stringValue == null) {
|
||||||
|
stringValue = new String(value);
|
||||||
|
}
|
||||||
|
value = null; // Clear the char array to save memory
|
||||||
|
}
|
||||||
|
return stringValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
String str = stringValue; // Local ref copy to avoid race
|
||||||
|
if (str != null) {
|
||||||
|
return str.hashCode();
|
||||||
|
}
|
||||||
|
char[] arr = value; // Local ref copy to avoid race
|
||||||
|
if (arr != null) {
|
||||||
|
return Arrays.hashCode(arr);
|
||||||
|
}
|
||||||
|
return toString().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (!(obj instanceof CharSequence))
|
||||||
|
return false;
|
||||||
|
if (obj instanceof LazyString) {
|
||||||
|
LazyString other = (LazyString) obj;
|
||||||
|
String str = stringValue; // Local ref copy to avoid race
|
||||||
|
String otherStr = other.stringValue; // Local ref copy to avoid race
|
||||||
|
if (str != null && otherStr != null) {
|
||||||
|
return str.equals(otherStr);
|
||||||
|
} else if (str != null) {
|
||||||
|
return str.contentEquals((CharSequence) other);
|
||||||
|
} else if (otherStr != null) {
|
||||||
|
return otherStr.contentEquals((CharSequence) this);
|
||||||
|
}
|
||||||
|
// Both are LazyString without stringValue
|
||||||
|
char[] arr = value; // Local ref copy to avoid race
|
||||||
|
char[] otherArr = other.value; // Local ref copy to avoid race
|
||||||
|
if (arr != null && otherArr != null) {
|
||||||
|
return Arrays.equals(arr, otherArr);
|
||||||
|
}
|
||||||
|
// Fallback to string comparison
|
||||||
|
return toString().equals(other.toString());
|
||||||
|
}
|
||||||
|
return toString().contentEquals((CharSequence) obj);
|
||||||
|
}
|
||||||
|
}
|
29
src/test/java/com/grack/nanojson/JsonBuilderTest.java
Normal file
29
src/test/java/com/grack/nanojson/JsonBuilderTest.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package com.grack.nanojson;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class JsonBuilderTest {
|
||||||
|
@Test
|
||||||
|
void failureKeyInArray() {
|
||||||
|
assertThrows(JsonWriterException.class, () ->
|
||||||
|
new JsonBuilder<>(new JsonArray()).key("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failureKeyWhileKeyPending() {
|
||||||
|
assertThrows(JsonWriterException.class, () ->
|
||||||
|
new JsonBuilder<>(new JsonObject()).key("a").key("b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void separateKeyWriting() {
|
||||||
|
JsonObject actual = new JsonBuilder<>(new JsonObject()).key("a").value(1).key("b").value(2).done();
|
||||||
|
JsonObject expected = new JsonObject();
|
||||||
|
expected.put("a", 1);
|
||||||
|
expected.put("b", 2);
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
@@ -15,76 +15,71 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to test that numbers are correctly round-tripped.
|
* Attempts to test that numbers are correctly round-tripped.
|
||||||
*/
|
*/
|
||||||
public class JsonNumberTest {
|
class JsonNumberTest {
|
||||||
// CHECKSTYLE_OFF: MagicNumber
|
// CHECKSTYLE_OFF: MagicNumber
|
||||||
// CHECKSTYLE_OFF: JavadocMethod
|
// CHECKSTYLE_OFF: JavadocMethod
|
||||||
@Test
|
@Test
|
||||||
public void testBasicNumberRead() throws JsonParserException {
|
void basicNumberRead() throws JsonParserException {
|
||||||
JsonArray array = JsonParser.array().from("[1, 1.0, 1.00]");
|
JsonArray array = JsonParser.array().from("[1, 1.0, 1.00]");
|
||||||
assertEquals(Integer.class, array.get(0).getClass());
|
assertEquals(1, ((Number) array.get(0)).intValue());
|
||||||
assertEquals(Double.class, array.get(1).getClass());
|
assertEquals(1.0, ((Number) array.get(1)).doubleValue(), 0.0);
|
||||||
assertEquals(Double.class, array.get(2).getClass());
|
assertEquals(1.0, ((Number) array.get(2)).doubleValue(), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicNumberWrite() {
|
void basicNumberWrite() {
|
||||||
JsonArray array = JsonArray.from(1, 1.0, 1.0f);
|
JsonArray array = JsonArray.from(1, 1.0, 1.0f);
|
||||||
assertEquals("[1,1.0,1.0]", JsonWriter.string().array(array).done());
|
assertEquals("[1,1.0,1.0]", JsonWriter.string().array(array).done());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLargeIntRead() throws JsonParserException {
|
void largeIntRead() throws JsonParserException {
|
||||||
JsonArray array = JsonParser.array().from("[-300000000,300000000]");
|
JsonArray array = JsonParser.array().from("[-300000000,300000000]");
|
||||||
assertEquals(Integer.class, array.get(0).getClass());
|
assertEquals(-300000000, ((Number) array.get(0)).intValue());
|
||||||
assertEquals(-300000000, array.get(0));
|
assertEquals(300000000, ((Number) array.get(1)).intValue());
|
||||||
assertEquals(Integer.class, array.get(1).getClass());
|
|
||||||
assertEquals(300000000, array.get(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLargeIntWrite() {
|
void largeIntWrite() {
|
||||||
JsonArray array = JsonArray.from(-300000000, 300000000);
|
JsonArray array = JsonArray.from(-300000000, 300000000);
|
||||||
assertEquals("[-300000000,300000000]", JsonWriter.string().array(array)
|
assertEquals("[-300000000,300000000]", JsonWriter.string().array(array)
|
||||||
.done());
|
.done());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLongRead() throws JsonParserException {
|
void longRead() throws JsonParserException {
|
||||||
JsonArray array = JsonParser.array().from("[-3000000000,3000000000]");
|
JsonArray array = JsonParser.array().from("[-3000000000,3000000000]");
|
||||||
assertEquals(Long.class, array.get(0).getClass());
|
assertEquals(-3000000000L, ((Number) array.get(0)).longValue());
|
||||||
assertEquals(-3000000000L, array.get(0));
|
assertEquals(3000000000L, ((Number) array.get(1)).longValue());
|
||||||
assertEquals(Long.class, array.get(1).getClass());
|
|
||||||
assertEquals(3000000000L, array.get(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLongWrite() {
|
void longWrite() {
|
||||||
JsonArray array = JsonArray.from(1L, -3000000000L, 3000000000L);
|
JsonArray array = JsonArray.from(1L, -3000000000L, 3000000000L);
|
||||||
assertEquals("[1,-3000000000,3000000000]",
|
assertEquals("[1,-3000000000,3000000000]",
|
||||||
JsonWriter.string().array(array).done());
|
JsonWriter.string().array(array).done());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBigIntRead() throws JsonParserException {
|
void bigIntRead() throws JsonParserException {
|
||||||
JsonArray array = JsonParser.array().from(
|
JsonArray array = JsonParser.array().from(
|
||||||
"[-30000000000000000000,30000000000000000000]");
|
"[-30000000000000000000,30000000000000000000]");
|
||||||
assertEquals(BigInteger.class, array.get(0).getClass());
|
// cast to ensure it's a number
|
||||||
assertEquals(new BigInteger("-30000000000000000000"), array.get(0));
|
assertEquals("-30000000000000000000", ((Number) array.get(0)).toString());
|
||||||
assertEquals(BigInteger.class, array.get(1).getClass());
|
assertEquals("30000000000000000000", ((Number) array.get(1)).toString());
|
||||||
assertEquals(new BigInteger("30000000000000000000"), array.get(1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBigIntWrite() {
|
void bigIntWrite() {
|
||||||
JsonArray array = JsonArray.from(BigInteger.ONE, new BigInteger(
|
JsonArray array = JsonArray.from(BigInteger.ONE, new BigInteger(
|
||||||
"-30000000000000000000"),
|
"-30000000000000000000"),
|
||||||
new BigInteger("30000000000000000000"));
|
new BigInteger("30000000000000000000"));
|
||||||
@@ -96,7 +91,7 @@ public class JsonNumberTest {
|
|||||||
* Tests a bug where longs were silently truncated to floats.
|
* Tests a bug where longs were silently truncated to floats.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testLongBuilder() {
|
void longBuilder() {
|
||||||
JsonObject o = JsonObject.builder().value("long", 0xffffffffffffL)
|
JsonObject o = JsonObject.builder().value("long", 0xffffffffffffL)
|
||||||
.done();
|
.done();
|
||||||
assertEquals(0xffffffffffffL, o.getNumber("long").longValue());
|
assertEquals(0xffffffffffffL, o.getNumber("long").longValue());
|
||||||
@@ -106,7 +101,7 @@ public class JsonNumberTest {
|
|||||||
* Test around the edges of the integral types.
|
* Test around the edges of the integral types.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAroundEdges() throws JsonParserException {
|
void aroundEdges() throws JsonParserException {
|
||||||
JsonArray array = JsonArray.from(Integer.MAX_VALUE,
|
JsonArray array = JsonArray.from(Integer.MAX_VALUE,
|
||||||
((long) Integer.MAX_VALUE) + 1, Integer.MIN_VALUE,
|
((long) Integer.MAX_VALUE) + 1, Integer.MIN_VALUE,
|
||||||
((long) Integer.MIN_VALUE) - 1, Long.MAX_VALUE, BigInteger
|
((long) Integer.MIN_VALUE) - 1, Long.MAX_VALUE, BigInteger
|
||||||
@@ -122,4 +117,11 @@ public class JsonNumberTest {
|
|||||||
String json2 = JsonWriter.string().array(array2).done();
|
String json2 = JsonWriter.string().array(array2).done();
|
||||||
assertEquals(json, json2);
|
assertEquals(json, json2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void trailingDecimalLazy() throws JsonParserException {
|
||||||
|
Object value = JsonParser.any().withLazyNumbers().from("1.000");
|
||||||
|
String json = JsonWriter.string().value(value).done();
|
||||||
|
assertEquals("1.000", json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,29 +15,28 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for {@link JsonParser}.
|
* Test for {@link JsonParser}.
|
||||||
*/
|
*/
|
||||||
public class JsonParserTest {
|
class JsonParserTest {
|
||||||
private static final Charset UTF8;
|
private static final Charset UTF8;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -48,7 +47,7 @@ public class JsonParserTest {
|
|||||||
// CHECKSTYLE_OFF: JavadocMethod
|
// CHECKSTYLE_OFF: JavadocMethod
|
||||||
// CHECKSTYLE_OFF: EmptyBlock
|
// CHECKSTYLE_OFF: EmptyBlock
|
||||||
@Test
|
@Test
|
||||||
public void testWhitespace() throws JsonParserException {
|
void whitespace() throws JsonParserException {
|
||||||
assertEquals(JsonObject.class,
|
assertEquals(JsonObject.class,
|
||||||
JsonParser.object().from(" \t\r\n { \t\r\n \"abc\" \t\r\n : \t\r\n 1 \t\r\n } \t\r\n ")
|
JsonParser.object().from(" \t\r\n { \t\r\n \"abc\" \t\r\n : \t\r\n 1 \t\r\n } \t\r\n ")
|
||||||
.getClass());
|
.getClass());
|
||||||
@@ -56,15 +55,14 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWhitespaceSimpler() throws JsonParserException {
|
void whitespaceSimpler() throws JsonParserException {
|
||||||
assertEquals(JsonObject.class,
|
assertEquals(JsonObject.class,
|
||||||
JsonParser.object().from(" {} ")
|
JsonParser.object().from(" {} ")
|
||||||
.getClass());
|
.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriterOutput() throws JsonParserException {
|
void writerOutput() throws JsonParserException {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
String json = JsonWriter.string()
|
String json = JsonWriter.string()
|
||||||
.object()
|
.object()
|
||||||
@@ -84,52 +82,50 @@ public class JsonParserTest {
|
|||||||
.end()
|
.end()
|
||||||
.done();
|
.done();
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
JsonParser.object().from(json); // ensure parseable
|
||||||
// Just make sure it can be read - don't validate
|
|
||||||
JsonParser.object().from(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyObject() throws JsonParserException {
|
void emptyObject() throws JsonParserException {
|
||||||
assertEquals(JsonObject.class, JsonParser.object().from("{}").getClass());
|
assertEquals(JsonObject.class, JsonParser.object().from("{}").getClass());
|
||||||
assertEquals("{}", JsonParser.object().from("{}").toString());
|
assertEquals("{}", JsonParser.object().from("{}").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testObjectOneElement() throws JsonParserException {
|
void objectOneElement() throws JsonParserException {
|
||||||
assertEquals(JsonObject.class, JsonParser.object().from("{\"a\":1}").getClass());
|
assertEquals(JsonObject.class, JsonParser.object().from("{\"a\":1}").getClass());
|
||||||
assertEquals("{a=1}", JsonParser.object().from("{\"a\":1}").toString());
|
assertEquals("{a=1}", JsonParser.object().from("{\"a\":1}").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testObjectTwoElements() throws JsonParserException {
|
void objectTwoElements() throws JsonParserException {
|
||||||
JsonObject obj = JsonParser.object().from("{\"a\":1,\"B\":1}");
|
JsonObject obj = JsonParser.object().from("{\"a\":1,\"B\":1}");
|
||||||
assertEquals(JsonObject.class, obj.getClass());
|
assertEquals(JsonObject.class, obj.getClass());
|
||||||
assertEquals(1, obj.get("B"));
|
assertEquals(1, obj.getInt("B"));
|
||||||
assertEquals(1, obj.get("a"));
|
assertEquals(1, obj.getInt("a"));
|
||||||
assertEquals(2, obj.size());
|
assertEquals(2, obj.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyArray() throws JsonParserException {
|
void emptyArray() throws JsonParserException {
|
||||||
assertEquals(JsonArray.class, JsonParser.array().from("[]").getClass());
|
assertEquals(JsonArray.class, JsonParser.array().from("[]").getClass());
|
||||||
assertEquals("[]", JsonParser.array().from("[]").toString());
|
assertEquals("[]", JsonParser.array().from("[]").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayOneElement() throws JsonParserException {
|
void arrayOneElement() throws JsonParserException {
|
||||||
assertEquals(JsonArray.class, JsonParser.array().from("[1]").getClass());
|
assertEquals(JsonArray.class, JsonParser.array().from("[1]").getClass());
|
||||||
assertEquals("[1]", JsonParser.array().from("[1]").toString());
|
assertEquals("[1]", JsonParser.array().from("[1]").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayTwoElements() throws JsonParserException {
|
void arrayTwoElements() throws JsonParserException {
|
||||||
assertEquals(JsonArray.class, JsonParser.array().from("[1,1]").getClass());
|
assertEquals(JsonArray.class, JsonParser.array().from("[1,1]").getClass());
|
||||||
assertEquals("[1, 1]", JsonParser.array().from("[1,1]").toString());
|
assertEquals("[1, 1]", JsonParser.array().from("[1,1]").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicTypes() throws JsonParserException {
|
void basicTypes() throws JsonParserException {
|
||||||
assertEquals("true", JsonParser.any().from("true").toString());
|
assertEquals("true", JsonParser.any().from("true").toString());
|
||||||
assertEquals("false", JsonParser.any().from("false").toString());
|
assertEquals("false", JsonParser.any().from("false").toString());
|
||||||
assertNull(JsonParser.any().from("null"));
|
assertNull(JsonParser.any().from("null"));
|
||||||
@@ -140,9 +136,9 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayWithEverything() throws JsonParserException {
|
void arrayWithEverything() throws JsonParserException {
|
||||||
JsonArray a = JsonParser.array().from("[1, -1.0e6, \"abc\", [1,2,3], {\"abc\":123}, true, false]");
|
JsonArray a = JsonParser.array().from("[1, -1.0e6, \"abc\", [1,2,3], {\"abc\":123}, true, false]");
|
||||||
assertEquals("[1, -1000000.0, abc, [1, 2, 3], {abc=123}, true, false]", a.toString());
|
assertEquals("[1, -1.0e6, abc, [1, 2, 3], {abc=123}, true, false]", a.toString());
|
||||||
assertEquals(1.0, a.getDouble(0), 0.001f);
|
assertEquals(1.0, a.getDouble(0), 0.001f);
|
||||||
assertEquals(1, a.getInt(0));
|
assertEquals(1, a.getInt(0));
|
||||||
assertEquals(-1000000, a.getInt(1));
|
assertEquals(-1000000, a.getInt(1));
|
||||||
@@ -155,94 +151,95 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testObjectWithEverything() throws JsonParserException {
|
void objectWithEverything() throws JsonParserException {
|
||||||
// TODO: Is this deterministic if we use string keys?
|
// TODO: Is this deterministic if we use string keys?
|
||||||
JsonObject o = JsonParser.object().from(
|
JsonObject o = JsonParser.object().from(
|
||||||
"{\"abc\":123, \"def\":456.0, \"ghi\":[true, false], \"jkl\":null, \"mno\":true}");
|
"{\"abc\":123, \"def\":456.0, \"ghi\":[true, false], \"jkl\":null, \"mno\":true}");
|
||||||
|
|
||||||
assertEquals(null, o.get("jkl"));
|
assertNull(o.get("jkl"));
|
||||||
assertTrue(o.containsKey("jkl"));
|
assertTrue(o.containsKey("jkl"));
|
||||||
assertEquals(123, o.get("abc"));
|
assertEquals(123, ((Number) o.get("abc")).intValue());
|
||||||
assertEquals(Arrays.asList(true, false), o.get("ghi"));
|
assertEquals(Arrays.asList(true, false), o.get("ghi"));
|
||||||
assertEquals(456.0, o.get("def"));
|
assertEquals(456.0, ((Number) o.get("def")).doubleValue());
|
||||||
assertEquals(true, o.get("mno"));
|
assertEquals(true, o.get("mno"));
|
||||||
assertEquals(5, o.size());
|
assertEquals(5, o.size());
|
||||||
|
|
||||||
assertEquals(123, o.getInt("abc"));
|
assertEquals(123, o.getInt("abc"));
|
||||||
assertEquals(456, o.getInt("def"));
|
assertEquals(456, o.getInt("def"));
|
||||||
assertEquals(true, o.getArray("ghi").getBoolean(0));
|
assertTrue(o.getArray("ghi").getBoolean(0));
|
||||||
assertEquals(null, o.get("jkl"));
|
assertNull(o.get("jkl"));
|
||||||
assertTrue(o.isNull("jkl"));
|
assertTrue(o.isNull("jkl"));
|
||||||
assertTrue(o.getBoolean("mno"));
|
assertTrue(o.getBoolean("mno"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStringEscapes() throws JsonParserException {
|
void stringEscapes() throws JsonParserException {
|
||||||
assertEquals("\n", JsonParser.any().from("\"\\n\""));
|
assertEquals("\n", JsonParser.any().from("\"\\n\"").toString());
|
||||||
assertEquals("\r", JsonParser.any().from("\"\\r\""));
|
assertEquals("\r", JsonParser.any().from("\"\\r\"").toString());
|
||||||
assertEquals("\t", JsonParser.any().from("\"\\t\""));
|
assertEquals("\t", JsonParser.any().from("\"\\t\"").toString());
|
||||||
assertEquals("\b", JsonParser.any().from("\"\\b\""));
|
assertEquals("\b", JsonParser.any().from("\"\\b\"").toString());
|
||||||
assertEquals("\f", JsonParser.any().from("\"\\f\""));
|
assertEquals("\f", JsonParser.any().from("\"\\f\"").toString());
|
||||||
assertEquals("/", JsonParser.any().from("\"/\""));
|
assertEquals("/", JsonParser.any().from("\"/\"").toString());
|
||||||
assertEquals("\\", JsonParser.any().from("\"\\\\\""));
|
assertEquals("\\", JsonParser.any().from("\"\\\\\"").toString());
|
||||||
assertEquals("\"", JsonParser.any().from("\"\\\"\""));
|
assertEquals("\"", JsonParser.any().from("\"\\\"\"").toString());
|
||||||
assertEquals("\0", JsonParser.any().from("\"\\u0000\""));
|
assertEquals("\0", JsonParser.any().from("\"\\u0000\"").toString());
|
||||||
assertEquals("\u8000", JsonParser.any().from("\"\\u8000\""));
|
assertEquals("\u8000", JsonParser.any().from("\"\\u8000\"").toString());
|
||||||
assertEquals("\uffff", JsonParser.any().from("\"\\uffff\""));
|
assertEquals("\uffff", JsonParser.any().from("\"\\uffff\"").toString());
|
||||||
assertEquals("\uFFFF", JsonParser.any().from("\"\\uFFFF\""));
|
assertEquals("\uFFFF", JsonParser.any().from("\"\\uFFFF\"").toString());
|
||||||
|
|
||||||
assertEquals("all together: \\/\n\r\t\b\f (fin)",
|
assertEquals("all together: \\/\n\r\t\b\f (fin)",
|
||||||
JsonParser.any().from("\"all together: \\\\\\/\\n\\r\\t\\b\\f (fin)\""));
|
JsonParser.any().from("\"all together: \\\\\\/\\n\\r\\t\\b\\f (fin)\"").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStringEscapesAroundBufferBoundary() throws JsonParserException {
|
void stringEscapesAroundBufferBoundary() throws JsonParserException {
|
||||||
char[] c = new char[JsonTokener.BUFFER_SIZE - 1024];
|
char[] c = new char[JsonTokener.BUFFER_SIZE - 1024];
|
||||||
Arrays.fill(c, ' ');
|
Arrays.fill(c, ' ');
|
||||||
String base = new String(c);
|
String base = new String(c);
|
||||||
for (int i = 0; i < 2048; i++) {
|
for (int i = 0; i < 2048; i++) {
|
||||||
base += " ";
|
base += " ";
|
||||||
assertEquals("\u0055", JsonParser.any().from(base + "\"\\u0055\""));
|
assertEquals("\u0055", JsonParser.any().from(base + "\"\\u0055\"").toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStringsAroundBufferBoundary() throws JsonParserException {
|
void stringsAroundBufferBoundary() throws JsonParserException {
|
||||||
char[] c = new char[JsonTokener.BUFFER_SIZE - 16];
|
char[] c = new char[JsonTokener.BUFFER_SIZE - 16];
|
||||||
Arrays.fill(c, ' ');
|
Arrays.fill(c, ' ');
|
||||||
String base = new String(c);
|
String base = new String(c);
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
base += " ";
|
base += " ";
|
||||||
assertEquals(base, JsonParser.any().from('"' + base + '"'));
|
assertEquals(base, JsonParser.any().from('"' + base + '"').toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNumbers() throws JsonParserException {
|
void numbers() throws JsonParserException {
|
||||||
String[] testCases = new String[] { "0", "1", "-0", "-1", "0.1", "1.1", "-0.1", "0.10", "-0.10", "0e1", "0e0",
|
String[] testCases = new String[] { "0", "1", "-0", "-1", "0.1", "1.1", "-0.1", "0.10", "-0.10", "0e1", "0e0",
|
||||||
"-0e-1", "0.0e0", "-0.0e0", "9" };
|
"-0e-1", "0.0e0", "-0.0e0", "9" };
|
||||||
for (String testCase : testCases) {
|
for (String testCase : testCases) {
|
||||||
Number n = (Number)JsonParser.any().from(testCase);
|
Number n = (Number) JsonParser.any().from(testCase);
|
||||||
assertEquals(Double.parseDouble(testCase), n.doubleValue(), Double.MIN_NORMAL);
|
assertEquals(Double.parseDouble(testCase), n.doubleValue(), Double.MIN_NORMAL);
|
||||||
Number n2 = (Number)JsonParser.any().from(testCase.toUpperCase());
|
Number n2 = (Number) JsonParser.any().from(testCase.toUpperCase());
|
||||||
assertEquals(Double.parseDouble(testCase.toUpperCase()), n2.doubleValue(), Double.MIN_NORMAL);
|
assertEquals(Double.parseDouble(testCase.toUpperCase()), n2.doubleValue(), Double.MIN_NORMAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that negative zero ends up as negative zero in both the parser and the writer.
|
* Test that negative zero ends up as negative zero in both the parser and the
|
||||||
|
* writer.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNegativeZero() throws JsonParserException {
|
void negativeZero() throws JsonParserException {
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0.0")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0.0")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0.0e0")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0.0e0")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0e0")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0e0")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0e1")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0e1")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0e-1")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0e-1")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0e-0")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0e-0")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0e-01")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0e-01")).doubleValue()));
|
||||||
assertEquals("-0.0", Double.toString(((Number)JsonParser.any().from("-0e-000000000001")).doubleValue()));
|
assertEquals("-0.0", Double.toString(((Number) JsonParser.any().from("-0e-000000000001")).doubleValue()));
|
||||||
|
|
||||||
assertEquals("-0.0", JsonWriter.string(-0.0));
|
assertEquals("-0.0", JsonWriter.string(-0.0));
|
||||||
assertEquals("-0.0", JsonWriter.string(-0.0f));
|
assertEquals("-0.0", JsonWriter.string(-0.0f));
|
||||||
@@ -252,21 +249,24 @@ public class JsonParserTest {
|
|||||||
* Test the basic numbers from -100 to 100 as a sanity check.
|
* Test the basic numbers from -100 to 100 as a sanity check.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBasicNumbers() throws JsonParserException {
|
void basicNumbers() throws JsonParserException {
|
||||||
for (int i = -100; i <= +100; i++) {
|
for (int i = -100; i <= +100; i++) {
|
||||||
assertEquals(i, (int)(Integer)JsonParser.any().from("" + i));
|
Number n = (Number) JsonParser.any().from(Integer.toString(i));
|
||||||
|
assertEquals(i, n.intValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBigint() throws JsonParserException {
|
void bigint() throws JsonParserException {
|
||||||
JsonObject o = JsonParser.object().from("{\"v\":123456789123456789123456789}");
|
JsonObject o = JsonParser.object().from("{\"v\":123456789123456789123456789}");
|
||||||
BigInteger bigint = (BigInteger)o.get("v");
|
Object raw = o.get("v");
|
||||||
assertEquals("123456789123456789123456789", bigint.toString());
|
// May be parsed as JsonLazyNumber or BigInteger depending on laziness settings
|
||||||
|
String s = raw.toString();
|
||||||
|
assertEquals("123456789123456789123456789", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailWrongType() {
|
void failWrongType() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("1");
|
JsonParser.object().from("1");
|
||||||
fail("Should have failed to parse");
|
fail("Should have failed to parse");
|
||||||
@@ -276,7 +276,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailNull() {
|
void failNull() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("null");
|
JsonParser.object().from("null");
|
||||||
fail("Should have failed to parse");
|
fail("Should have failed to parse");
|
||||||
@@ -286,7 +286,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailNoJson1() {
|
void failNoJson1() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("");
|
JsonParser.object().from("");
|
||||||
fail("Should have failed to parse");
|
fail("Should have failed to parse");
|
||||||
@@ -296,7 +296,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailNoJson2() {
|
void failNoJson2() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from(" ");
|
JsonParser.object().from(" ");
|
||||||
fail("Should have failed to parse");
|
fail("Should have failed to parse");
|
||||||
@@ -306,7 +306,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailNoJson3() {
|
void failNoJson3() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from(" ");
|
JsonParser.object().from(" ");
|
||||||
fail("Should have failed to parse");
|
fail("Should have failed to parse");
|
||||||
@@ -316,7 +316,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailNumberEdgeCases() {
|
void failNumberEdgeCases() {
|
||||||
String[] edgeCases = { "-", ".", "e", "01", "-01", "+01", "01.1", "-01.1", "+01.1", ".1", "-.1", "+.1", "+1",
|
String[] edgeCases = { "-", ".", "e", "01", "-01", "+01", "01.1", "-01.1", "+01.1", ".1", "-.1", "+.1", "+1",
|
||||||
"0.", "-0.", "+0.", "0.e", "-0.e", "+0.e", "0e", "-0e", "+0e", "0e-", "-0e-", "+0e-", "0e+", "-0e+",
|
"0.", "-0.", "+0.", "0.e", "-0.e", "+0.e", "0e", "-0e", "+0e", "0e-", "-0e-", "+0e-", "0e+", "-0e+",
|
||||||
"+0e+", "-e", "+e", "2.", "-2.", "-1.e1", "1.e1", "0.e1" };
|
"+0e+", "-e", "+e", "2.", "-2.", "-1.e1", "1.e1", "0.e1" };
|
||||||
@@ -347,10 +347,11 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See http://seriot.ch/json/parsing.html and https://github.com/mmastrac/nanojson/issues/3.
|
* See http://seriot.ch/json/parsing.html and
|
||||||
|
* https://github.com/mmastrac/nanojson/issues/3.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFailNumberEdgeCasesFromJSONSuite() {
|
void failNumberEdgeCasesFromJSONSuite() {
|
||||||
String[] edgeCases = { "[-2.]", "[0.e1]", "[2.e+3]", "[2.e-3]", "[2.e3]", "[1.]" };
|
String[] edgeCases = { "[-2.]", "[0.e1]", "[2.e+3]", "[2.e-3]", "[2.e3]", "[1.]" };
|
||||||
for (String edgeCase : edgeCases) {
|
for (String edgeCase : edgeCases) {
|
||||||
try {
|
try {
|
||||||
@@ -363,10 +364,11 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See http://seriot.ch/json/parsing.html and https://github.com/mmastrac/nanojson/issues/3.
|
* See http://seriot.ch/json/parsing.html and
|
||||||
|
* https://github.com/mmastrac/nanojson/issues/3.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFailNumberEdgeCasesFromJSONSuiteNoArray() {
|
void failNumberEdgeCasesFromJSONSuiteNoArray() {
|
||||||
String[] edgeCases = { "-2.", "0.e1", "2.e+3", "2.e-3", "2.e3", "1." };
|
String[] edgeCases = { "-2.", "0.e1", "2.e+3", "2.e-3", "2.e3", "1." };
|
||||||
for (String edgeCase : edgeCases) {
|
for (String edgeCase : edgeCases) {
|
||||||
try {
|
try {
|
||||||
@@ -379,7 +381,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedNumber1() {
|
void failBustedNumber1() {
|
||||||
try {
|
try {
|
||||||
// There's no 'f' in double, but it treats it as a new token
|
// There's no 'f' in double, but it treats it as a new token
|
||||||
JsonParser.object().from("123f");
|
JsonParser.object().from("123f");
|
||||||
@@ -390,7 +392,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedNumber2() {
|
void failBustedNumber2() {
|
||||||
try {
|
try {
|
||||||
// Badly formed number
|
// Badly formed number
|
||||||
JsonParser.object().from("-1-1");
|
JsonParser.object().from("-1-1");
|
||||||
@@ -401,7 +403,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString1() {
|
void failBustedString1() {
|
||||||
try {
|
try {
|
||||||
// Missing " at end
|
// Missing " at end
|
||||||
JsonParser.object().from("\"abc");
|
JsonParser.object().from("\"abc");
|
||||||
@@ -412,7 +414,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString2() {
|
void failBustedString2() {
|
||||||
try {
|
try {
|
||||||
// \n in middle of string
|
// \n in middle of string
|
||||||
JsonParser.object().from("\"abc\n\"");
|
JsonParser.object().from("\"abc\n\"");
|
||||||
@@ -423,7 +425,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString3() {
|
void failBustedString3() {
|
||||||
try {
|
try {
|
||||||
// Bad escape "\x" in middle of string
|
// Bad escape "\x" in middle of string
|
||||||
JsonParser.object().from("\"abc\\x\"");
|
JsonParser.object().from("\"abc\\x\"");
|
||||||
@@ -434,7 +436,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString4() {
|
void failBustedString4() {
|
||||||
try {
|
try {
|
||||||
// Bad escape "\\u123x" in middle of string
|
// Bad escape "\\u123x" in middle of string
|
||||||
JsonParser.object().from("\"\\u123x\"");
|
JsonParser.object().from("\"\\u123x\"");
|
||||||
@@ -445,7 +447,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString5() {
|
void failBustedString5() {
|
||||||
try {
|
try {
|
||||||
// Incomplete unicode escape
|
// Incomplete unicode escape
|
||||||
JsonParser.object().from("\"\\u222\"");
|
JsonParser.object().from("\"\\u222\"");
|
||||||
@@ -456,7 +458,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString6() {
|
void failBustedString6() {
|
||||||
try {
|
try {
|
||||||
// String that terminates halfway through a unicode escape
|
// String that terminates halfway through a unicode escape
|
||||||
JsonParser.object().from("\"\\u222");
|
JsonParser.object().from("\"\\u222");
|
||||||
@@ -467,7 +469,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBustedString7() {
|
void failBustedString7() {
|
||||||
try {
|
try {
|
||||||
// String that terminates halfway through a regular escape
|
// String that terminates halfway through a regular escape
|
||||||
JsonParser.object().from("\"\\");
|
JsonParser.object().from("\"\\");
|
||||||
@@ -478,7 +480,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailArrayTrailingComma1() {
|
void failArrayTrailingComma1() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("[,]");
|
JsonParser.object().from("[,]");
|
||||||
fail();
|
fail();
|
||||||
@@ -488,7 +490,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailArrayTrailingComma2() {
|
void failArrayTrailingComma2() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("[1,]");
|
JsonParser.object().from("[1,]");
|
||||||
fail();
|
fail();
|
||||||
@@ -498,7 +500,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectTrailingComma1() {
|
void failObjectTrailingComma1() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{,}");
|
JsonParser.object().from("{,}");
|
||||||
fail();
|
fail();
|
||||||
@@ -508,7 +510,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectTrailingComma2() {
|
void failObjectTrailingComma2() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{\"abc\":123,}");
|
JsonParser.object().from("{\"abc\":123,}");
|
||||||
fail();
|
fail();
|
||||||
@@ -518,7 +520,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectBadKey1() {
|
void failObjectBadKey1() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{true:1}");
|
JsonParser.object().from("{true:1}");
|
||||||
fail();
|
fail();
|
||||||
@@ -528,7 +530,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectBadKey2() {
|
void failObjectBadKey2() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{2:1}");
|
JsonParser.object().from("{2:1}");
|
||||||
fail();
|
fail();
|
||||||
@@ -538,7 +540,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectBadColon1() {
|
void failObjectBadColon1() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{\"abc\":}");
|
JsonParser.object().from("{\"abc\":}");
|
||||||
fail();
|
fail();
|
||||||
@@ -548,7 +550,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectBadColon2() {
|
void failObjectBadColon2() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{\"abc\":1:}");
|
JsonParser.object().from("{\"abc\":1:}");
|
||||||
fail();
|
fail();
|
||||||
@@ -558,7 +560,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailObjectBadColon3() {
|
void failObjectBadColon3() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("{:\"abc\":1}");
|
JsonParser.object().from("{:\"abc\":1}");
|
||||||
fail();
|
fail();
|
||||||
@@ -568,7 +570,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords1() {
|
void failBadKeywords1() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("truef");
|
JsonParser.object().from("truef");
|
||||||
fail();
|
fail();
|
||||||
@@ -578,7 +580,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords2() {
|
void failBadKeywords2() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("true1");
|
JsonParser.object().from("true1");
|
||||||
fail();
|
fail();
|
||||||
@@ -588,7 +590,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords3() {
|
void failBadKeywords3() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("tru");
|
JsonParser.object().from("tru");
|
||||||
fail();
|
fail();
|
||||||
@@ -598,7 +600,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords4() {
|
void failBadKeywords4() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("[truef,true]");
|
JsonParser.object().from("[truef,true]");
|
||||||
fail();
|
fail();
|
||||||
@@ -608,7 +610,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords5() {
|
void failBadKeywords5() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("grue");
|
JsonParser.object().from("grue");
|
||||||
fail();
|
fail();
|
||||||
@@ -618,7 +620,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords6() {
|
void failBadKeywords6() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("trueeeeeeeeeeeeeeeeeeee");
|
JsonParser.object().from("trueeeeeeeeeeeeeeeeeeee");
|
||||||
fail();
|
fail();
|
||||||
@@ -628,7 +630,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailBadKeywords7() {
|
void failBadKeywords7() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from("g");
|
JsonParser.object().from("g");
|
||||||
fail();
|
fail();
|
||||||
@@ -638,7 +640,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailTrailingCommaMultiline() {
|
void failTrailingCommaMultiline() {
|
||||||
String testString = "{\n\"abc\":123,\n\"def\":456,\n}";
|
String testString = "{\n\"abc\":123,\n\"def\":456,\n}";
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from(testString);
|
JsonParser.object().from(testString);
|
||||||
@@ -652,7 +654,7 @@ public class JsonParserTest {
|
|||||||
* Ensures that we're correctly tracking UTF-8 character positions.
|
* Ensures that we're correctly tracking UTF-8 character positions.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFailTrailingCommaUTF8() {
|
void failTrailingCommaUTF8() {
|
||||||
ByteArrayInputStream in1 = new ByteArrayInputStream("{\n\"abc\":123,\"def\":456,}".getBytes(Charset
|
ByteArrayInputStream in1 = new ByteArrayInputStream("{\n\"abc\":123,\"def\":456,}".getBytes(Charset
|
||||||
.forName("UTF-8")));
|
.forName("UTF-8")));
|
||||||
ByteArrayInputStream in2 = new ByteArrayInputStream(
|
ByteArrayInputStream in2 = new ByteArrayInputStream(
|
||||||
@@ -676,58 +678,58 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodingUTF8() throws JsonParserException {
|
void encodingUTF8() throws JsonParserException {
|
||||||
testEncoding(UTF8);
|
testEncoding(UTF8);
|
||||||
testEncodingBOM(UTF8);
|
testEncodingBOM(UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodingUTF16LE() throws JsonParserException {
|
void encodingUTF16LE() throws JsonParserException {
|
||||||
Charset charset = Charset.forName("UTF-16LE");
|
Charset charset = Charset.forName("UTF-16LE");
|
||||||
testEncoding(charset);
|
testEncoding(charset);
|
||||||
testEncodingBOM(charset);
|
testEncodingBOM(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodingUTF16BE() throws JsonParserException {
|
void encodingUTF16BE() throws JsonParserException {
|
||||||
Charset charset = Charset.forName("UTF-16BE");
|
Charset charset = Charset.forName("UTF-16BE");
|
||||||
testEncoding(charset);
|
testEncoding(charset);
|
||||||
testEncodingBOM(charset);
|
testEncodingBOM(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodingUTF32LE() throws JsonParserException {
|
void encodingUTF32LE() throws JsonParserException {
|
||||||
Charset charset = Charset.forName("UTF-32LE");
|
Charset charset = Charset.forName("UTF-32LE");
|
||||||
testEncoding(charset);
|
testEncoding(charset);
|
||||||
testEncodingBOM(charset);
|
testEncodingBOM(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodingUTF32BE() throws JsonParserException {
|
void encodingUTF32BE() throws JsonParserException {
|
||||||
Charset charset = Charset.forName("UTF-32BE");
|
Charset charset = Charset.forName("UTF-32BE");
|
||||||
testEncoding(charset);
|
testEncoding(charset);
|
||||||
testEncodingBOM(charset);
|
testEncodingBOM(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidUTF8Codepoint() throws JsonParserException {
|
void validUTF8Codepoint() throws JsonParserException {
|
||||||
assertEquals("\ud83d\ude8a",
|
assertEquals("\ud83d\ude8a",
|
||||||
JsonParser.any().from(new ByteArrayInputStream("\"\ud83d\ude8a\"".getBytes(UTF8))));
|
JsonParser.any().from(new ByteArrayInputStream("\"\ud83d\ude8a\"".getBytes(UTF8))).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidUTF8Codepoint2() throws JsonParserException {
|
void validUTF8Codepoint2() throws JsonParserException {
|
||||||
assertEquals("\u2602",
|
assertEquals("\u2602",
|
||||||
JsonParser.any().from(new ByteArrayInputStream("\"\u2602\"".getBytes(UTF8))));
|
JsonParser.any().from(new ByteArrayInputStream("\"\u2602\"".getBytes(UTF8))).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIllegalUTF8Bytes() {
|
void illegalUTF8Bytes() {
|
||||||
// Test the always-illegal bytes
|
// Test the always-illegal bytes
|
||||||
int[] failures = new int[] { 0xc0, 0xc1, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
|
int[] failures = new int[] { 0xc0, 0xc1, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
|
||||||
for (int i = 0; i < failures.length; i++) {
|
for (int i = 0; i < failures.length; i++) {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from(new ByteArrayInputStream(new byte[] { '"', (byte)failures[i], '"' }));
|
JsonParser.object().from(new ByteArrayInputStream(new byte[] { '"', (byte) failures[i], '"' }));
|
||||||
} catch (JsonParserException e) {
|
} catch (JsonParserException e) {
|
||||||
testException(e, 1, 2, "UTF-8");
|
testException(e, 1, 2, "UTF-8");
|
||||||
}
|
}
|
||||||
@@ -736,7 +738,7 @@ public class JsonParserTest {
|
|||||||
// Test the continuation bytes outside of a continuation
|
// Test the continuation bytes outside of a continuation
|
||||||
for (int i = 0x80; i <= 0xBF; i++) {
|
for (int i = 0x80; i <= 0xBF; i++) {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from(new ByteArrayInputStream(new byte[] { '"', (byte)i, '"' }));
|
JsonParser.object().from(new ByteArrayInputStream(new byte[] { '"', (byte) i, '"' }));
|
||||||
} catch (JsonParserException e) {
|
} catch (JsonParserException e) {
|
||||||
testException(e, 1, 2, "UTF-8");
|
testException(e, 1, 2, "UTF-8");
|
||||||
}
|
}
|
||||||
@@ -744,10 +746,11 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See http://seriot.ch/parsing_json.html and https://github.com/mmastrac/nanojson/issues/3.
|
* See http://seriot.ch/parsing_json.html and
|
||||||
|
* https://github.com/mmastrac/nanojson/issues/3.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testIllegalUTF8StringFromJSONSuite() {
|
void illegalUTF8StringFromJSONSuite() {
|
||||||
try {
|
try {
|
||||||
JsonParser.object().from(new ByteArrayInputStream(new byte[] {
|
JsonParser.object().from(new ByteArrayInputStream(new byte[] {
|
||||||
'"', (byte) 0xed, (byte) 0xa0, (byte) 0x80, '"' }));
|
'"', (byte) 0xed, (byte) 0xa0, (byte) 0x80, '"' }));
|
||||||
@@ -774,7 +777,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void failureTestsFromYui() throws IOException {
|
void failureTestsFromYui() throws IOException {
|
||||||
InputStream input = getClass().getClassLoader().getResourceAsStream("yui_fail_cases.txt");
|
InputStream input = getClass().getClassLoader().getResourceAsStream("yui_fail_cases.txt");
|
||||||
|
|
||||||
String[] failCases = readAsUtf8(input).split("\n");
|
String[] failCases = readAsUtf8(input).split("\n");
|
||||||
@@ -789,7 +792,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tortureTest() throws JsonParserException, IOException {
|
void tortureTest() throws JsonParserException, IOException {
|
||||||
InputStream input = getClass().getClassLoader().getResourceAsStream("sample.json");
|
InputStream input = getClass().getClassLoader().getResourceAsStream("sample.json");
|
||||||
JsonObject o = JsonParser.object().from(readAsUtf8(input));
|
JsonObject o = JsonParser.object().from(readAsUtf8(input));
|
||||||
assertNotNull(o.get("a"));
|
assertNotNull(o.get("a"));
|
||||||
@@ -803,26 +806,26 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tortureTestUrl() throws JsonParserException {
|
void tortureTestUrl() throws JsonParserException {
|
||||||
JsonObject o = JsonParser.object().from(getClass().getClassLoader().getResource("sample.json"));
|
JsonObject o = JsonParser.object().from(getClass().getClassLoader().getResource("sample.json"));
|
||||||
assertNotNull(o.getObject("a").getArray("b\uecee\u8324\u007a\\\ue768.N"));
|
assertNotNull(o.getObject("a").getArray("b\uecee\u8324\u007a\\\ue768.N"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void tortureTestStream() throws JsonParserException {
|
void tortureTestStream() throws JsonParserException {
|
||||||
JsonObject o = JsonParser.object().from(getClass().getClassLoader().getResourceAsStream("sample.json"));
|
JsonObject o = JsonParser.object().from(getClass().getClassLoader().getResourceAsStream("sample.json"));
|
||||||
assertNotNull(o.getObject("a").getArray("b\uecee\u8324\u007a\\\ue768.N"));
|
assertNotNull(o.getObject("a").getArray("b\uecee\u8324\u007a\\\ue768.N"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIssue38() throws JsonParserException, IOException {
|
void issue38() throws JsonParserException, IOException {
|
||||||
// https://github.com/mmastrac/nanojson/issues/38
|
// https://github.com/mmastrac/nanojson/issues/38
|
||||||
InputStream input = getClass().getClassLoader().getResourceAsStream("issue-38.json");
|
InputStream input = getClass().getClassLoader().getResourceAsStream("issue-38.json");
|
||||||
JsonParser.any().from(readAsUtf8(input));
|
JsonParser.any().from(readAsUtf8(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEscapeSequencesAcrossBufferBoundary() throws JsonParserException {
|
void escapeSequencesAcrossBufferBoundary() throws JsonParserException {
|
||||||
String s1 = "";
|
String s1 = "";
|
||||||
String s2 = "";
|
String s2 = "";
|
||||||
|
|
||||||
@@ -839,7 +842,7 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailTruncatedEscapeAcrossBufferBoundary() {
|
void failTruncatedEscapeAcrossBufferBoundary() {
|
||||||
String s1 = "\\u123";
|
String s1 = "\\u123";
|
||||||
String s2 = "";
|
String s2 = "";
|
||||||
for (int i = 0; i < 126; i++) {
|
for (int i = 0; i < 126; i++) {
|
||||||
@@ -856,7 +859,7 @@ public class JsonParserTest {
|
|||||||
JsonParser.object().from("\"" + s2 + s1);
|
JsonParser.object().from("\"" + s2 + s1);
|
||||||
fail();
|
fail();
|
||||||
} catch (JsonParserException e) {
|
} catch (JsonParserException e) {
|
||||||
assertTrue(e.getMessage(), e.getMessage().contains("EOF"));
|
assertTrue(e.getMessage().contains("EOF"), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -867,7 +870,7 @@ public class JsonParserTest {
|
|||||||
* Skips two tests that don't match reality (ie: Chrome).
|
* Skips two tests that don't match reality (ie: Chrome).
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void jsonOrgTest() throws IOException {
|
void jsonOrgTest() throws IOException {
|
||||||
InputStream input = getClass().getClassLoader().getResourceAsStream("json_org_test.zip");
|
InputStream input = getClass().getClassLoader().getResourceAsStream("json_org_test.zip");
|
||||||
ZipInputStream zip = new ZipInputStream(input);
|
ZipInputStream zip = new ZipInputStream(input);
|
||||||
ZipEntry ze;
|
ZipEntry ze;
|
||||||
@@ -886,7 +889,7 @@ public class JsonParserTest {
|
|||||||
|
|
||||||
boolean positive = ze.getName().startsWith("test/pass");
|
boolean positive = ze.getName().startsWith("test/pass");
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int size = (int)ze.getSize();
|
int size = (int) ze.getSize();
|
||||||
byte[] buffer = new byte[size];
|
byte[] buffer = new byte[size];
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
int r = zip.read(buffer, offset, buffer.length - offset);
|
int r = zip.read(buffer, offset, buffer.length - offset);
|
||||||
@@ -931,14 +934,14 @@ public class JsonParserTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testException(JsonParserException e, int linePos, int charPos) {
|
private void testException(JsonParserException e, int linePos, int charPos) {
|
||||||
assertEquals(e.getMessage() + " incorrect location",
|
assertEquals("line " + linePos + " char " + charPos,
|
||||||
"line " + linePos + " char " + charPos,
|
"line " + e.getLinePosition() + " char " + e.getCharPosition(),
|
||||||
"line " + e.getLinePosition() + " char " + e.getCharPosition());
|
e.getMessage() + " incorrect location");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testException(JsonParserException e, int linePos, int charPos, String inError) {
|
private void testException(JsonParserException e, int linePos, int charPos, String inError) {
|
||||||
assertEquals("line " + linePos + " char " + charPos,
|
assertEquals("line " + linePos + " char " + charPos,
|
||||||
"line " + e.getLinePosition() + " char " + e.getCharPosition());
|
"line " + e.getLinePosition() + " char " + e.getCharPosition());
|
||||||
assertTrue("Error did not contain '" + inError + "': " + e.getMessage(), e.getMessage().contains(inError));
|
assertTrue(e.getMessage().contains(inError), "Error did not contain '" + inError + "': " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -25,9 +25,9 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import com.grack.nanojson.Users.Friend;
|
import com.grack.nanojson.Users.Friend;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import com.grack.nanojson.Users.User;
|
import com.grack.nanojson.Users.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +39,7 @@ public class JsonReaderTest {
|
|||||||
* Read a simple object.
|
* Read a simple object.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testObject() throws JsonParserException {
|
void object() throws JsonParserException {
|
||||||
JsonReader reader = JsonReader.from("{\"a\":1}");
|
JsonReader reader = JsonReader.from("{\"a\":1}");
|
||||||
assertEquals(JsonReader.Type.OBJECT, reader.current());
|
assertEquals(JsonReader.Type.OBJECT, reader.current());
|
||||||
reader.object();
|
reader.object();
|
||||||
@@ -54,7 +54,7 @@ public class JsonReaderTest {
|
|||||||
* Read a simple array.
|
* Read a simple array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testArray() throws JsonParserException {
|
void array() throws JsonParserException {
|
||||||
JsonReader reader = JsonReader.from("[\"a\",1,null]");
|
JsonReader reader = JsonReader.from("[\"a\",1,null]");
|
||||||
assertEquals(JsonReader.Type.ARRAY, reader.current());
|
assertEquals(JsonReader.Type.ARRAY, reader.current());
|
||||||
reader.array();
|
reader.array();
|
||||||
@@ -77,7 +77,7 @@ public class JsonReaderTest {
|
|||||||
* Assert all the things.
|
* Assert all the things.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedDetailed() throws JsonParserException {
|
void nestedDetailed() throws JsonParserException {
|
||||||
String json = createNestedJson();
|
String json = createNestedJson();
|
||||||
|
|
||||||
JsonReader reader = JsonReader.from(json);
|
JsonReader reader = JsonReader.from(json);
|
||||||
@@ -128,11 +128,11 @@ public class JsonReaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same test as {@link JsonReaderTest#testNestedDetailed()}, less assertions to get a better
|
* Same test as {@link JsonReaderTest#nestedDetailed()}, less assertions to get a better
|
||||||
* feel for the API.
|
* feel for the API.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedLight() throws JsonParserException {
|
void nestedLight() throws JsonParserException {
|
||||||
String json = createNestedJson();
|
String json = createNestedJson();
|
||||||
|
|
||||||
JsonReader reader = JsonReader.from(json);
|
JsonReader reader = JsonReader.from(json);
|
||||||
@@ -176,7 +176,7 @@ public class JsonReaderTest {
|
|||||||
* Test reading an multiple arrays (including an empty one) in a object.
|
* Test reading an multiple arrays (including an empty one) in a object.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testArraysInObject() throws JsonParserException {
|
void arraysInObject() throws JsonParserException {
|
||||||
String json = createArraysInObject();
|
String json = createArraysInObject();
|
||||||
JsonReader reader = JsonReader.from(json);
|
JsonReader reader = JsonReader.from(json);
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ public class JsonReaderTest {
|
|||||||
* Test the {@link Users} class from java-json-benchmark.
|
* Test the {@link Users} class from java-json-benchmark.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testJsonBenchmarkUser() throws JsonParserException {
|
void jsonBenchmarkUser() throws JsonParserException {
|
||||||
JsonReader reader = JsonReader.from(getClass().getResourceAsStream("/users.json"));
|
JsonReader reader = JsonReader.from(getClass().getResourceAsStream("/users.json"));
|
||||||
|
|
||||||
parseUsers(reader);
|
parseUsers(reader);
|
||||||
|
@@ -15,24 +15,26 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for the various JSON types.
|
* Test for the various JSON types.
|
||||||
*/
|
*/
|
||||||
public class JsonTypesTest {
|
class JsonTypesTest {
|
||||||
// CHECKSTYLE_OFF: MagicNumber
|
// CHECKSTYLE_OFF: MagicNumber
|
||||||
// CHECKSTYLE_OFF: JavadocMethod
|
// CHECKSTYLE_OFF: JavadocMethod
|
||||||
@Test
|
@Test
|
||||||
public void testObjectInt() {
|
void objectInt() {
|
||||||
JsonObject o = new JsonObject();
|
JsonObject o = new JsonObject();
|
||||||
o.put("key", 1);
|
o.put("key", 1);
|
||||||
assertEquals(1, o.getInt("key"));
|
assertEquals(1, o.getInt("key"));
|
||||||
@@ -42,39 +44,39 @@ public class JsonTypesTest {
|
|||||||
assertEquals(1, o.getNumber("key"));
|
assertEquals(1, o.getNumber("key"));
|
||||||
assertEquals(1, o.get("key"));
|
assertEquals(1, o.get("key"));
|
||||||
|
|
||||||
assertEquals(null, o.getString("key"));
|
assertNull(o.getString("key"));
|
||||||
assertEquals("foo", o.getString("key", "foo"));
|
assertEquals("foo", o.getString("key", "foo"));
|
||||||
assertFalse(o.isNull("key"));
|
assertFalse(o.isNull("key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testObjectString() {
|
void objectString() {
|
||||||
JsonObject o = new JsonObject();
|
JsonObject o = new JsonObject();
|
||||||
o.put("key", "1");
|
o.put("key", "1");
|
||||||
assertEquals(0, o.getInt("key"));
|
assertEquals(0, o.getInt("key"));
|
||||||
assertEquals(0L, o.getLong("key"));
|
assertEquals(0L, o.getLong("key"));
|
||||||
assertEquals(0, o.getDouble("key"), 0.0001f);
|
assertEquals(0, o.getDouble("key"), 0.0001f);
|
||||||
assertEquals(0f, o.getFloat("key"), 0.0001f);
|
assertEquals(0f, o.getFloat("key"), 0.0001f);
|
||||||
assertEquals(null, o.getNumber("key"));
|
assertNull(o.getNumber("key"));
|
||||||
assertEquals("1", o.get("key"));
|
assertEquals("1", o.get("key"));
|
||||||
assertFalse(o.isNull("key"));
|
assertFalse(o.isNull("key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testObjectNull() {
|
void objectNull() {
|
||||||
JsonObject o = new JsonObject();
|
JsonObject o = new JsonObject();
|
||||||
o.put("key", null);
|
o.put("key", null);
|
||||||
assertEquals(0, o.getInt("key"));
|
assertEquals(0, o.getInt("key"));
|
||||||
assertEquals(0L, o.getLong("key"));
|
assertEquals(0L, o.getLong("key"));
|
||||||
assertEquals(0, o.getDouble("key"), 0.0001f);
|
assertEquals(0, o.getDouble("key"), 0.0001f);
|
||||||
assertEquals(0f, o.getFloat("key"), 0.0001f);
|
assertEquals(0f, o.getFloat("key"), 0.0001f);
|
||||||
assertEquals(null, o.getNumber("key"));
|
assertNull(o.getNumber("key"));
|
||||||
assertEquals(null, o.get("key"));
|
assertNull(o.get("key"));
|
||||||
assertTrue(o.isNull("key"));
|
assertTrue(o.isNull("key"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayInt() {
|
void arrayInt() {
|
||||||
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
||||||
null));
|
null));
|
||||||
o.set(3, 1);
|
o.set(3, 1);
|
||||||
@@ -85,13 +87,13 @@ public class JsonTypesTest {
|
|||||||
assertEquals(1, o.getNumber(3));
|
assertEquals(1, o.getNumber(3));
|
||||||
assertEquals(1, o.get(3));
|
assertEquals(1, o.get(3));
|
||||||
|
|
||||||
assertEquals(null, o.getString(3));
|
assertNull(o.getString(3));
|
||||||
assertEquals("foo", o.getString(3, "foo"));
|
assertEquals("foo", o.getString(3, "foo"));
|
||||||
assertFalse(o.isNull(3));
|
assertFalse(o.isNull(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayString() {
|
void arrayString() {
|
||||||
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
||||||
null));
|
null));
|
||||||
o.set(3, "1");
|
o.set(3, "1");
|
||||||
@@ -99,40 +101,40 @@ public class JsonTypesTest {
|
|||||||
assertEquals(0L, o.getLong(3));
|
assertEquals(0L, o.getLong(3));
|
||||||
assertEquals(0, o.getDouble(3), 0.0001f);
|
assertEquals(0, o.getDouble(3), 0.0001f);
|
||||||
assertEquals(0, o.getFloat(3), 0.0001f);
|
assertEquals(0, o.getFloat(3), 0.0001f);
|
||||||
assertEquals(null, o.getNumber(3));
|
assertNull(o.getNumber(3));
|
||||||
assertEquals("1", o.get(3));
|
assertEquals("1", o.get(3));
|
||||||
assertFalse(o.isNull(3));
|
assertFalse(o.isNull(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayNull() {
|
void arrayNull() {
|
||||||
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
||||||
null));
|
null));
|
||||||
o.set(3, null);
|
o.set(3, null);
|
||||||
assertEquals(0, o.getInt(3));
|
assertEquals(0, o.getInt(3));
|
||||||
assertEquals(0, o.getDouble(3), 0.0001f);
|
assertEquals(0, o.getDouble(3), 0.0001f);
|
||||||
assertEquals(0, o.getFloat(3), 0.0001f);
|
assertEquals(0, o.getFloat(3), 0.0001f);
|
||||||
assertEquals(null, o.getNumber(3));
|
assertNull(o.getNumber(3));
|
||||||
assertEquals(null, o.get(3));
|
assertNull(o.get(3));
|
||||||
assertTrue(o.isNull(3));
|
assertTrue(o.isNull(3));
|
||||||
assertTrue(o.has(3));
|
assertTrue(o.has(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testArrayBounds() {
|
void arrayBounds() {
|
||||||
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
JsonArray o = new JsonArray(Arrays.asList((String) null, null, null,
|
||||||
null));
|
null));
|
||||||
assertEquals(0, o.getInt(4));
|
assertEquals(0, o.getInt(4));
|
||||||
assertEquals(0, o.getDouble(4), 0.0001f);
|
assertEquals(0, o.getDouble(4), 0.0001f);
|
||||||
assertEquals(0, o.getFloat(4), 0.0001f);
|
assertEquals(0, o.getFloat(4), 0.0001f);
|
||||||
assertEquals(null, o.getNumber(4));
|
assertNull(o.getNumber(4));
|
||||||
assertEquals(null, o.get(4));
|
assertNull(o.get(4));
|
||||||
assertFalse(o.isNull(4));
|
assertFalse(o.isNull(4));
|
||||||
assertFalse(o.has(4));
|
assertFalse(o.has(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJsonArrayBuilder() {
|
void jsonArrayBuilder() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
JsonArray a = JsonArray.builder().value(true).value(1.0).value(1.0f)
|
JsonArray a = JsonArray.builder().value(true).value(1.0).value(1.0f)
|
||||||
.value(1).value(new BigInteger("1234567890")).value("hi")
|
.value(1).value(new BigInteger("1234567890")).value("hi")
|
||||||
@@ -147,7 +149,7 @@ public class JsonTypesTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJsonObjectBuilder() {
|
void jsonObjectBuilder() {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
JsonObject a = JsonObject
|
JsonObject a = JsonObject
|
||||||
.builder()
|
.builder()
|
||||||
@@ -185,23 +187,26 @@ public class JsonTypesTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = JsonWriterException.class)
|
@Test
|
||||||
public void testJsonArrayBuilderFailCantCloseRoot() {
|
void jsonArrayBuilderFailCantCloseRoot() {
|
||||||
JsonArray.builder().end();
|
assertThrows(JsonWriterException.class, () ->
|
||||||
}
|
JsonArray.builder().end());
|
||||||
|
|
||||||
@Test(expected = JsonWriterException.class)
|
|
||||||
public void testJsonArrayBuilderFailCantAddKeyToArray() {
|
|
||||||
JsonArray.builder().value("abc", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = JsonWriterException.class)
|
|
||||||
public void testJsonArrayBuilderFailCantAddNonKeyToObject() {
|
|
||||||
JsonObject.builder().value(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJsonKeyOrder() {
|
void jsonArrayBuilderFailCantAddKeyToArray() {
|
||||||
|
assertThrows(JsonWriterException.class, () ->
|
||||||
|
JsonArray.builder().value("abc", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jsonArrayBuilderFailCantAddNonKeyToObject() {
|
||||||
|
assertThrows(JsonWriterException.class, () ->
|
||||||
|
JsonObject.builder().value(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void jsonKeyOrder() {
|
||||||
JsonObject a = JsonObject
|
JsonObject a = JsonObject
|
||||||
.builder()
|
.builder()
|
||||||
.value("key01", 1)
|
.value("key01", 1)
|
||||||
|
@@ -15,9 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.grack.nanojson;
|
package com.grack.nanojson;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@@ -25,25 +23,32 @@ import java.io.PrintStream;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
import org.junit.Test;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for {@link JsonWriter}.
|
* Test for {@link JsonWriter}.
|
||||||
*/
|
*/
|
||||||
public class JsonWriterTest {
|
class JsonWriterTest {
|
||||||
private static final Charset UTF8 = StandardCharsets.UTF_8;
|
private static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||||
|
|
||||||
// CHECKSTYLE_OFF: MagicNumber
|
// CHECKSTYLE_OFF: MagicNumber
|
||||||
// CHECKSTYLE_OFF: JavadocMethod
|
// CHECKSTYLE_OFF: JavadocMethod
|
||||||
// CHECKSTYLE_OFF: EmptyBlock
|
// CHECKSTYLE_OFF: EmptyBlock
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test emitting simple values.
|
* Test emitting simple values.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleValues() {
|
void simpleValues() {
|
||||||
assertEquals("true", JsonWriter.string().value(true).done());
|
assertEquals("true", JsonWriter.string().value(true).done());
|
||||||
assertEquals("null", JsonWriter.string().nul().done());
|
assertEquals("null", JsonWriter.string().nul().done());
|
||||||
assertEquals("1.0", JsonWriter.string().value(1.0).done());
|
assertEquals("1.0", JsonWriter.string().value(1.0).done());
|
||||||
@@ -57,16 +62,16 @@ public class JsonWriterTest {
|
|||||||
* exception.
|
* exception.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testStreamWriterWithNonBMPStringAroundBufferSize() throws JsonParserException {
|
void streamWriterWithNonBMPStringAroundBufferSize() throws JsonParserException {
|
||||||
char[] c = new char[JsonWriterBase.BUFFER_SIZE - 128];
|
char[] c = new char[JsonWriterBase.BUFFER_SIZE - 128];
|
||||||
Arrays.fill(c, ' ');
|
Arrays.fill(c, ' ');
|
||||||
String base = new String(c);
|
StringBuilder base = new StringBuilder(new String(c));
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
base += " ";
|
base.append(" ");
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
String s = base + new String(new int[] { 0x10ffff }, 0, 1);
|
String s = base + new String(new int[]{0x10ffff}, 0, 1);
|
||||||
JsonWriter.on(bytes).value(s).done();
|
JsonWriter.on(bytes).value(s).done();
|
||||||
assertEquals(s, JsonParser.any().from(new String(bytes.toByteArray(), UTF8)));
|
assertEquals(s, ((LazyString) JsonParser.any().from(new String(bytes.toByteArray(), UTF8))).toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,16 +80,17 @@ public class JsonWriterTest {
|
|||||||
* exception.
|
* exception.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testStreamWriterWithBMPStringAroundBufferSize() throws JsonParserException {
|
void streamWriterWithBMPStringAroundBufferSize() throws JsonParserException {
|
||||||
char[] c = new char[JsonWriterBase.BUFFER_SIZE - 128];
|
char[] c = new char[JsonWriterBase.BUFFER_SIZE - 128];
|
||||||
Arrays.fill(c, ' ');
|
Arrays.fill(c, ' ');
|
||||||
String base = new String(c);
|
String base = new String(c);
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
base += " ";
|
base += " ";
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
String s = base + new String(new int[] { 0xffff }, 0, 1);
|
String s = base + new String(new int[]{0xffff}, 0, 1);
|
||||||
JsonWriter.on(bytes).value(s).done();
|
JsonWriter.on(bytes).value(s).done();
|
||||||
assertEquals(s, JsonParser.any().from(new String(bytes.toByteArray(), UTF8)));
|
Object parsed = JsonParser.any().from(new String(bytes.toByteArray(), UTF8));
|
||||||
|
assertEquals(s, parsed instanceof LazyString ? parsed.toString() : parsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,19 +99,20 @@ public class JsonWriterTest {
|
|||||||
* boundary exception.
|
* boundary exception.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testStreamWriterWithArrayAroundBufferSize() throws JsonParserException {
|
void streamWriterWithArrayAroundBufferSize() throws JsonParserException {
|
||||||
char[] c = new char[JsonWriterBase.BUFFER_SIZE - 128];
|
char[] c = new char[JsonWriterBase.BUFFER_SIZE - 128];
|
||||||
Arrays.fill(c, ' ');
|
Arrays.fill(c, ' ');
|
||||||
String base = new String(c);
|
String base = new String(c);
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < 256; i++) {
|
||||||
base += " ";
|
base += " ";
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
String s = base + new String(new int[] { 0x10ffff }, 0, 1);
|
String s = base + new String(new int[]{0x10ffff}, 0, 1);
|
||||||
JsonWriter.on(bytes).array().value(s).nul().end().done();
|
JsonWriter.on(bytes).array().value(s).nul().end().done();
|
||||||
String s2 = new String(bytes.toByteArray(), UTF8);
|
String s2 = new String(bytes.toByteArray(), UTF8);
|
||||||
JsonArray array = JsonParser.array().from(s2);
|
JsonArray array = JsonParser.array().from(s2);
|
||||||
assertEquals(s, array.get(0));
|
Object first = array.get(0);
|
||||||
assertEquals(null, array.get(1));
|
assertEquals(s, first instanceof LazyString ? first.toString() : first);
|
||||||
|
assertNull(array.get(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +120,7 @@ public class JsonWriterTest {
|
|||||||
* Test various ways of writing null, as well as various situations.
|
* Test various ways of writing null, as well as various situations.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNull() {
|
void testNull() {
|
||||||
assertEquals("null", JsonWriter.string().value((String) null).done());
|
assertEquals("null", JsonWriter.string().value((String) null).done());
|
||||||
assertEquals("null", JsonWriter.string().value((Number) null).done());
|
assertEquals("null", JsonWriter.string().value((Number) null).done());
|
||||||
assertEquals("null", JsonWriter.string().nul().done());
|
assertEquals("null", JsonWriter.string().nul().done());
|
||||||
@@ -132,11 +139,21 @@ public class JsonWriterTest {
|
|||||||
.end().done());
|
.end().done());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void separateKeyWriting() {
|
||||||
|
assertEquals("{\"a\":null}",
|
||||||
|
JsonWriter.string().object().key("a").value((Number) null).end()
|
||||||
|
.done());
|
||||||
|
assertEquals("{\"a\":{\"b\":null}}",
|
||||||
|
JsonWriter.string().object().key("a").object().value("b", (Number) null)
|
||||||
|
.end().end().done());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test escaping of chars < 256.
|
* Test escaping of chars < 256.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testStringControlCharacters() {
|
void stringControlCharacters() {
|
||||||
StringBuilder chars = new StringBuilder();
|
StringBuilder chars = new StringBuilder();
|
||||||
for (int i = 0; i < 0xa0; i++)
|
for (int i = 0; i < 0xa0; i++)
|
||||||
chars.append((char) i);
|
chars.append((char) i);
|
||||||
@@ -157,7 +174,7 @@ public class JsonWriterTest {
|
|||||||
* Test escaping of chars < 256.
|
* Test escaping of chars < 256.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEscape() {
|
void escape() {
|
||||||
StringBuilder chars = new StringBuilder();
|
StringBuilder chars = new StringBuilder();
|
||||||
for (int i = 0; i < 0xa0; i++)
|
for (int i = 0; i < 0xa0; i++)
|
||||||
chars.append((char) i);
|
chars.append((char) i);
|
||||||
@@ -177,10 +194,10 @@ public class JsonWriterTest {
|
|||||||
* Torture test for UTF8 character encoding.
|
* Torture test for UTF8 character encoding.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBMPCharacters() throws Exception {
|
void bmpCharacters() throws Exception {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
for (int i = 0; i < 0xD000; i++) {
|
for (int i = 0; i < 0xD000; i++) {
|
||||||
builder.append((char)i);
|
builder.append((char) i);
|
||||||
}
|
}
|
||||||
builder.append("\ue000");
|
builder.append("\ue000");
|
||||||
builder.append("\uefff");
|
builder.append("\uefff");
|
||||||
@@ -189,26 +206,28 @@ public class JsonWriterTest {
|
|||||||
|
|
||||||
// Base string
|
// Base string
|
||||||
String s = JsonWriter.string(builder.toString());
|
String s = JsonWriter.string(builder.toString());
|
||||||
assertEquals(builder.toString(), (String)JsonParser.any().from(s));
|
Object parsed = JsonParser.any().from(s);
|
||||||
|
assertEquals(builder.toString(), parsed instanceof LazyString ? parsed.toString() : parsed);
|
||||||
|
|
||||||
// Ensure that it also matches the PrintStream output
|
// Ensure that it also matches the PrintStream output
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(new PrintStream(bytes, false, "UTF-8")).value(builder.toString()).done();
|
JsonWriter.on(new PrintStream(bytes, false, "UTF-8")).value(builder.toString()).done();
|
||||||
assertEquals(builder.toString(), (String)JsonParser.any().from(new String(bytes.toByteArray(),
|
parsed = JsonParser.any().from(new String(bytes.toByteArray(), UTF8));
|
||||||
UTF8)));
|
assertEquals(builder.toString(), parsed instanceof LazyString ? parsed.toString() : parsed);
|
||||||
|
|
||||||
// Ensure that it also matches the stream output
|
// Ensure that it also matches the stream output
|
||||||
bytes = new ByteArrayOutputStream();
|
bytes = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(bytes).value(builder.toString()).done();
|
JsonWriter.on(bytes).value(builder.toString()).done();
|
||||||
assertEquals(builder.toString(), (String)JsonParser.any().from(new String(bytes.toByteArray(),
|
parsed = JsonParser.any().from(new String(bytes.toByteArray(), UTF8));
|
||||||
UTF8)));
|
assertEquals(builder.toString(), parsed instanceof LazyString ? parsed.toString() : parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Torture test for UTF8 character encoding outside the basic multilingual plane.
|
* Torture test for UTF8 character encoding outside the basic multilingual
|
||||||
|
* plane.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNonBMP() throws Exception {
|
void nonBMP() throws Exception {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
builder.appendCodePoint(0x10000); // Start of non-BMP
|
builder.appendCodePoint(0x10000); // Start of non-BMP
|
||||||
builder.appendCodePoint(0x1f601); // GRINNING FACE WITH SMILING EYES
|
builder.appendCodePoint(0x1f601); // GRINNING FACE WITH SMILING EYES
|
||||||
@@ -216,26 +235,26 @@ public class JsonWriterTest {
|
|||||||
|
|
||||||
// Base string
|
// Base string
|
||||||
String s = JsonWriter.string(builder.toString());
|
String s = JsonWriter.string(builder.toString());
|
||||||
assertEquals(builder.toString(), (String)JsonParser.any().from(s));
|
assertEquals(builder.toString(), ((LazyString) JsonParser.any().from(s)).toString());
|
||||||
|
|
||||||
// Ensure that it also matches the PrintStream output
|
// Ensure that it also matches the PrintStream output
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(new PrintStream(bytes, false, "UTF-8")).value(builder.toString()).done();
|
JsonWriter.on(new PrintStream(bytes, false, "UTF-8")).value(builder.toString()).done();
|
||||||
assertEquals(builder.toString(), (String)JsonParser.any().from(new String(bytes.toByteArray(),
|
assertEquals(builder.toString(), ((LazyString) JsonParser.any().from(new String(bytes.toByteArray(),
|
||||||
UTF8)));
|
UTF8))).toString());
|
||||||
|
|
||||||
// Ensure that it also matches the stream output
|
// Ensure that it also matches the stream output
|
||||||
bytes = new ByteArrayOutputStream();
|
bytes = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(bytes).value(builder.toString()).done();
|
JsonWriter.on(bytes).value(builder.toString()).done();
|
||||||
assertEquals(builder.toString(), (String)JsonParser.any().from(new String(bytes.toByteArray(),
|
assertEquals(builder.toString(), ((LazyString) JsonParser.any().from(new String(bytes.toByteArray(),
|
||||||
UTF8)));
|
UTF8))).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic {@link OutputStream} smoke test.
|
* Basic {@link OutputStream} smoke test.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWriteToUTF8Stream() {
|
void writeToUTF8Stream() {
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(bytes).object().value("a\n", 1)
|
JsonWriter.on(bytes).object().value("a\n", 1)
|
||||||
.value("b", 2).end().done();
|
.value("b", 2).end().done();
|
||||||
@@ -247,7 +266,7 @@ public class JsonWriterTest {
|
|||||||
* Basic {@link PrintStream} smoke test.
|
* Basic {@link PrintStream} smoke test.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWriteToSystemOutLikeStream() throws Exception {
|
void writeToSystemOutLikeStream() throws Exception {
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(new PrintStream(bytes, false, "UTF-8")).object().value("a\n", 1)
|
JsonWriter.on(new PrintStream(bytes, false, "UTF-8")).object().value("a\n", 1)
|
||||||
.value("b", 2).end().done();
|
.value("b", 2).end().done();
|
||||||
@@ -260,7 +279,7 @@ public class JsonWriterTest {
|
|||||||
* Test escaping of / when following < to handle </script>.
|
* Test escaping of / when following < to handle </script>.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testScriptEndEscaping() {
|
void scriptEndEscaping() {
|
||||||
assertEquals("\"<\\/script>\"", JsonWriter.string("</script>"));
|
assertEquals("\"<\\/script>\"", JsonWriter.string("</script>"));
|
||||||
assertEquals("\"/script\"", JsonWriter.string("/script"));
|
assertEquals("\"/script\"", JsonWriter.string("/script"));
|
||||||
}
|
}
|
||||||
@@ -269,7 +288,7 @@ public class JsonWriterTest {
|
|||||||
* Test a simple array.
|
* Test a simple array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testArray() {
|
void array() {
|
||||||
String json = JsonWriter.string().array().value(true).value(false)
|
String json = JsonWriter.string().array().value(true).value(false)
|
||||||
.value(true).end().done();
|
.value(true).end().done();
|
||||||
assertEquals("[true,false,true]", json);
|
assertEquals("[true,false,true]", json);
|
||||||
@@ -279,16 +298,59 @@ public class JsonWriterTest {
|
|||||||
* Test an empty array.
|
* Test an empty array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testArrayEmpty() {
|
void arrayEmpty() {
|
||||||
String json = JsonWriter.string().array().end().done();
|
String json = JsonWriter.string().array().end().done();
|
||||||
assertEquals("[]", json);
|
assertEquals("[]", json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the auto-conversion of Writables.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void writable() {
|
||||||
|
assertEquals("null", JsonWriter.string((JsonConvertible) () -> null));
|
||||||
|
assertEquals("[]", JsonWriter.string((JsonConvertible) ArrayList::new));
|
||||||
|
assertEquals("{}", JsonWriter.string((JsonConvertible) HashMap::new));
|
||||||
|
assertEquals("\"\"", JsonWriter.string((JsonConvertible) () -> ""));
|
||||||
|
assertEquals("1", JsonWriter.string((JsonConvertible) () -> Integer.valueOf(1)));
|
||||||
|
assertEquals("1.0", JsonWriter.string((JsonConvertible) () -> Double.valueOf(1.0)));
|
||||||
|
assertEquals("1", JsonWriter.string((JsonConvertible) () -> Long.valueOf(1)));
|
||||||
|
assertEquals("1.0", JsonWriter.string((JsonConvertible) () -> Float.valueOf(1.0f)));
|
||||||
|
assertEquals(
|
||||||
|
"[null,[1,2,3],{\"a\":1,\"b\":2.0,\"c\":\"a\",\"d\":null,\"e\":[]}]",
|
||||||
|
JsonWriter.string((JsonConvertible) () -> (JsonConvertible) () -> {
|
||||||
|
ArrayList<Object> list = new ArrayList<>();
|
||||||
|
list.add(null);
|
||||||
|
list.add((JsonConvertible) () -> new int[]{1, 2, 3});
|
||||||
|
list.add((JsonConvertible) () -> {
|
||||||
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
map.put("a", 1);
|
||||||
|
map.put("b", 2.0);
|
||||||
|
map.put("c", "a");
|
||||||
|
map.put("d", null);
|
||||||
|
map.put("e", (JsonConvertible) ArrayList::new);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
return list;
|
||||||
|
}));
|
||||||
|
assertEquals(
|
||||||
|
"Unable to handle type: class java.lang.Object",
|
||||||
|
assertThrows(
|
||||||
|
JsonWriterException.class,
|
||||||
|
() -> JsonWriter.string((JsonConvertible) Object::new)).getMessage());
|
||||||
|
assertEquals(
|
||||||
|
"Unable to handle type: class java.lang.Object",
|
||||||
|
assertThrows(
|
||||||
|
JsonWriterException.class,
|
||||||
|
() -> JsonWriter.string((JsonConvertible) () -> Arrays.asList("d", 1, new Object())))
|
||||||
|
.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test an array of empty arrays.
|
* Test an array of empty arrays.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testArrayOfEmpty() {
|
void arrayOfEmpty() {
|
||||||
String json = JsonWriter.string().array().array().end().array().end()
|
String json = JsonWriter.string().array().array().end().array().end()
|
||||||
.end().done();
|
.end().done();
|
||||||
assertEquals("[[],[]]", json);
|
assertEquals("[[],[]]", json);
|
||||||
@@ -298,7 +360,7 @@ public class JsonWriterTest {
|
|||||||
* Test a nested array.
|
* Test a nested array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedArray() {
|
void nestedArray() {
|
||||||
String json = JsonWriter.string().array().array().array().value(true)
|
String json = JsonWriter.string().array().array().array().value(true)
|
||||||
.value(false).value(true).end().end().end().done();
|
.value(false).value(true).end().end().end().done();
|
||||||
assertEquals("[[[true,false,true]]]", json);
|
assertEquals("[[[true,false,true]]]", json);
|
||||||
@@ -308,7 +370,7 @@ public class JsonWriterTest {
|
|||||||
* Test a nested array.
|
* Test a nested array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedArray2() {
|
void nestedArray2() {
|
||||||
String json = JsonWriter.string().array().value(true).array().array()
|
String json = JsonWriter.string().array().value(true).array().array()
|
||||||
.value(false).end().end().value(true).end().done();
|
.value(false).end().end().value(true).end().done();
|
||||||
assertEquals("[true,[[false]],true]", json);
|
assertEquals("[true,[[false]],true]", json);
|
||||||
@@ -318,7 +380,7 @@ public class JsonWriterTest {
|
|||||||
* Test a simple object.
|
* Test a simple object.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testObject() {
|
void object() {
|
||||||
String json = JsonWriter.string().object().value("a", true)
|
String json = JsonWriter.string().object().value("a", true)
|
||||||
.value("b", false).value("c", true).end().done();
|
.value("b", false).value("c", true).end().done();
|
||||||
assertEquals("{\"a\":true,\"b\":false,\"c\":true}", json);
|
assertEquals("{\"a\":true,\"b\":false,\"c\":true}", json);
|
||||||
@@ -328,7 +390,7 @@ public class JsonWriterTest {
|
|||||||
* Test a simple object with indent.
|
* Test a simple object with indent.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testObjectIndent() {
|
void objectIndent() {
|
||||||
String json = JsonWriter.indent(" ").string().object()
|
String json = JsonWriter.indent(" ").string().object()
|
||||||
.value("a", true).value("b", false).value("c", true).end()
|
.value("a", true).value("b", false).value("c", true).end()
|
||||||
.done();
|
.done();
|
||||||
@@ -339,7 +401,7 @@ public class JsonWriterTest {
|
|||||||
* Test a nested object.
|
* Test a nested object.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedObject() {
|
void nestedObject() {
|
||||||
String json = JsonWriter.string().object().object("a")
|
String json = JsonWriter.string().object().object("a")
|
||||||
.value("b", false).value("c", true).end().end().done();
|
.value("b", false).value("c", true).end().end().done();
|
||||||
assertEquals("{\"a\":{\"b\":false,\"c\":true}}", json);
|
assertEquals("{\"a\":{\"b\":false,\"c\":true}}", json);
|
||||||
@@ -349,7 +411,7 @@ public class JsonWriterTest {
|
|||||||
* Test a nested object and array.
|
* Test a nested object and array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedObjectArray() {
|
void nestedObjectArray() {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
String json = JsonWriter.string()
|
String json = JsonWriter.string()
|
||||||
.object()
|
.object()
|
||||||
@@ -378,7 +440,7 @@ public class JsonWriterTest {
|
|||||||
* Test a nested object and array.
|
* Test a nested object and array.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testNestedObjectArrayIndent() {
|
void nestedObjectArrayIndent() {
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
String json = JsonWriter.indent(" ").string()
|
String json = JsonWriter.indent(" ").string()
|
||||||
.object()
|
.object()
|
||||||
@@ -402,14 +464,15 @@ public class JsonWriterTest {
|
|||||||
assertEquals(
|
assertEquals(
|
||||||
"{\n \"a\":{\n \"b\":[{\n \"a\":1,\n \"b\":2\n },{\n"
|
"{\n \"a\":{\n \"b\":[{\n \"a\":1,\n \"b\":2\n },{\n"
|
||||||
+ " \"c\":1.0,\n \"d\":2.0\n }],\n"
|
+ " \"c\":1.0,\n \"d\":2.0\n }],\n"
|
||||||
+ " \"c\":[\"a\",\"b\",\"c\"]\n }\n}", json);
|
+ " \"c\":[\"a\",\"b\",\"c\"]\n }\n}",
|
||||||
|
json);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the {@link Appendable} code.
|
* Tests the {@link Appendable} code.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testAppendable() {
|
void appendable() {
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
JsonWriter.on(writer).object().value("abc", "def").end().done();
|
JsonWriter.on(writer).object().value("abc", "def").end().done();
|
||||||
assertEquals("{\"abc\":\"def\"}", writer.toString());
|
assertEquals("{\"abc\":\"def\"}", writer.toString());
|
||||||
@@ -419,7 +482,7 @@ public class JsonWriterTest {
|
|||||||
* Tests the {@link OutputStream} code.
|
* Tests the {@link OutputStream} code.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testOutputStream() {
|
void outputStream() {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
JsonWriter.on(out).object().value("abc", "def").end().done();
|
JsonWriter.on(out).object().value("abc", "def").end().done();
|
||||||
assertEquals("{\"abc\":\"def\"}",
|
assertEquals("{\"abc\":\"def\"}",
|
||||||
@@ -427,64 +490,64 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickJson() {
|
void quickJson() {
|
||||||
assertEquals("true", JsonWriter.string(true));
|
assertEquals("true", JsonWriter.string(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickJsonArray() {
|
void quickJsonArray() {
|
||||||
assertEquals("[1,2,3]", JsonWriter.string(JsonArray.from(1, 2, 3)));
|
assertEquals("[1,2,3]", JsonWriter.string(JsonArray.from(1, 2, 3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickArray() {
|
void quickArray() {
|
||||||
assertEquals("[1,2,3]", JsonWriter.string(Arrays.asList(1, 2, 3)));
|
assertEquals("[1,2,3]", JsonWriter.string(Arrays.asList(1, 2, 3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickArrayEmpty() {
|
void quickArrayEmpty() {
|
||||||
assertEquals("[]", JsonWriter.string(Collections.emptyList()));
|
assertEquals("[]", JsonWriter.string(Collections.emptyList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickObjectArray() {
|
void quickObjectArray() {
|
||||||
assertEquals("[1,2,3]", JsonWriter.string(new Object[] { 1, 2, 3 }));
|
assertEquals("[1,2,3]", JsonWriter.string(new Object[]{1, 2, 3}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickObjectArrayNested() {
|
void quickObjectArrayNested() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"[[1,2],[[3]]]",
|
"[[1,2],[[3]]]",
|
||||||
JsonWriter.string(new Object[] { new Object[] { 1, 2 },
|
JsonWriter.string(new Object[]{new Object[]{1, 2},
|
||||||
new Object[] { new Object[] { 3 } } }));
|
new Object[]{new Object[]{3}}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuickObjectArrayEmpty() {
|
void quickObjectArrayEmpty() {
|
||||||
assertEquals("[]", JsonWriter.string(new Object[0]));
|
assertEquals("[]", JsonWriter.string(new Object[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testObjectArrayInMap() {
|
void objectArrayInMap() {
|
||||||
JsonObject o = new JsonObject();
|
JsonObject o = new JsonObject();
|
||||||
o.put("array of string", new String[] { "a", "b", "c" });
|
o.put("array of string", new String[]{"a", "b", "c"});
|
||||||
o.put("array of Boolean", new Boolean[] { true, false });
|
o.put("array of Boolean", new Boolean[]{true, false});
|
||||||
o.put("array of int", new int[] { 1, 2, 3 });
|
o.put("array of int", new int[]{1, 2, 3});
|
||||||
o.put("array of JsonObject",
|
o.put("array of JsonObject",
|
||||||
new JsonObject[] { new JsonObject(), null });
|
new JsonObject[]{new JsonObject(), null});
|
||||||
|
|
||||||
String[] bits = { "\"array of JsonObject\":[{},null]",
|
String[] bits = {"\"array of JsonObject\":[{},null]",
|
||||||
"\"array of Boolean\":[true,false]",
|
"\"array of Boolean\":[true,false]",
|
||||||
"\"array of string\":[\"a\",\"b\",\"c\"]",
|
"\"array of string\":[\"a\",\"b\",\"c\"]",
|
||||||
"\"array of int\":[1,2,3]" };
|
"\"array of int\":[1,2,3]"};
|
||||||
String s = JsonWriter.string(o);
|
String s = JsonWriter.string(o);
|
||||||
for (String bit : bits) {
|
for (String bit : bits) {
|
||||||
assertTrue("Didn't contain " + bit, s.contains(bit));
|
assertTrue(s.contains(bit), "Didn't contain " + bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureNoKeyInObject() {
|
void failureNoKeyInObject() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().object().value(true).end().done();
|
JsonWriter.string().object().value(true).end().done();
|
||||||
fail();
|
fail();
|
||||||
@@ -494,7 +557,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureNoKeyInObject2() {
|
void failureNoKeyInObject2() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().object().value("a", 1).value(true).end().done();
|
JsonWriter.string().object().value("a", 1).value(true).end().done();
|
||||||
fail();
|
fail();
|
||||||
@@ -504,7 +567,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureKeyInArray() {
|
void failureKeyInArray() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().array().value("x", true).end().done();
|
JsonWriter.string().array().value("x", true).end().done();
|
||||||
fail();
|
fail();
|
||||||
@@ -514,7 +577,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureKeyInArray2() {
|
void failureKeyInArray2() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().array().value(1).value("x", true).end().done();
|
JsonWriter.string().array().value(1).value("x", true).end().done();
|
||||||
fail();
|
fail();
|
||||||
@@ -524,7 +587,17 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureNotFullyClosed() {
|
void failureRepeatedKey() {
|
||||||
|
assertThrows(JsonWriterException.class, () -> JsonWriter.string().object().key("a").value("b", 2).end().done());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failureRepeatedKey2() {
|
||||||
|
assertThrows(JsonWriterException.class, () -> JsonWriter.string().object().key("a").key("b").end().done());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void failureNotFullyClosed() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().array().value(1).done();
|
JsonWriter.string().array().value(1).done();
|
||||||
fail();
|
fail();
|
||||||
@@ -534,7 +607,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureNotFullyClosed2() {
|
void failureNotFullyClosed2() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().array().done();
|
JsonWriter.string().array().done();
|
||||||
fail();
|
fail();
|
||||||
@@ -544,7 +617,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureEmpty() {
|
void failureEmpty() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().done();
|
JsonWriter.string().done();
|
||||||
fail();
|
fail();
|
||||||
@@ -554,7 +627,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureEmpty2() {
|
void failureEmpty2() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().end();
|
JsonWriter.string().end();
|
||||||
fail();
|
fail();
|
||||||
@@ -564,7 +637,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureMoreThanOneRoot() {
|
void failureMoreThanOneRoot() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().value(1).value(1).done();
|
JsonWriter.string().value(1).value(1).done();
|
||||||
fail();
|
fail();
|
||||||
@@ -574,7 +647,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureMoreThanOneRoot2() {
|
void failureMoreThanOneRoot2() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().array().value(1).end().value(1).done();
|
JsonWriter.string().array().value(1).end().value(1).done();
|
||||||
fail();
|
fail();
|
||||||
@@ -584,7 +657,7 @@ public class JsonWriterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailureMoreThanOneRoot3() {
|
void failureMoreThanOneRoot3() {
|
||||||
try {
|
try {
|
||||||
JsonWriter.string().array().value(1).end().array().value(1).end()
|
JsonWriter.string().array().value(1).end().array().value(1).end()
|
||||||
.done();
|
.done();
|
||||||
|
Reference in New Issue
Block a user