GithubHelp home page GithubHelp logo

nanojson's Introduction

nanojson Build Status Maven Central Language grade: Java

nanojson is a tiny, fast, and compliant JSON parser and writer for Java.

License

nanojson is dual-licensed under the MIT and Apache Public License.

Get started

  • Build: mvn clean compile test jar:jar
  • Javadocs: mvn javadoc:javadoc && open target/site/apidocs/index.html

Add it to your maven pom.xml:

<dependency>
  <groupId>com.grack</groupId>
  <artifactId>nanojson</artifactId>
  <version>1.9</version>
</dependency>

... or to your gradle file:

compile group: 'com.grack', name: 'nanojson', version: '1.7'

... or just drop the files directly into your project!

Features

Fast

  • Minimal object allocation
  • Fastest Java JSON in many cases: faster that Jackson when parsing from memory and in some streaming cases (with lazy numbers enabled):

Tiny

  • Minimal number of source lines: full parser around 800 lines, writer is around 500
  • Tiny jar: less than 25kB

Robust

  • Strict error checking, reasonable error messages
  • Well-tested: code-coverage-directed tests, passes more than 100 tests, including those from YUI and json.org

Easy to use

  • Well-documented
  • Apache licensed
  • No dependencies

Parser example

There are three entry points for parsing, depending on the type of JSON object you expect to parse: JsonParser.object().from(), JsonParser.array().from(), and JsonParser.any().from(). You pass them a String or a Reader and they will either return the parsed object of a given type or throw a JsonParserException.

JsonObject obj = JsonParser.object().from("{\"abc\":123}");
JsonArray array = JsonParser.array().from("[1,2,3]");
Number number = (Number)JsonParser.any().from("123.456e7");

Errors can be quickly located by using getLinePosition and getCharPosition on JsonParserException:

{
  "abc":123,
  "def":456,
}

com.grack.nanojson.JsonParserException: Trailing comma in object on line 4, char 1

For performance-sensitive code, numeric values can be parsed lazily using the withLazyNumbers option. JSON numeric values will then be parsed at access time rather than parse time:

JsonObject obj = JsonParser.object().withLazyNumbers().from("{\"abc\":123}");

Reader example

The JsonReader interface is a lower-level interface, but requires very few objects to be created when used correctly and is even faster than the standard JsonParser interface.

JsonReader reader = JsonReader.from(json);
reader.object();
assertTrue(reader.next());
assertEquals("a", reader.key());
reader.object();
assertTrue(reader.next());
assertEquals("b", reader.key());
reader.array();
// ...

The JsonReader interface could use some better documentation!

Writer example

JsonWriter is a simple, stateful JSON writer that can output to a String, or to anything implementing the Java Appendable interface. The latter includes StringBuilder, Writer, PrintStream, and CharBuffer.

JsonWriter has a straightforward interface: value methods for writing JSON literals such as numbers and strings, and array and object for managing array and object contexts. array, object and the value methods each have two overloads: one with a key prefix for writing objects and the other for writing raw JSON values or within an array.

String json = JsonWriter.string()
  .object()
     .array("a")
       .value(1)
       .value(2)
     .end()
     .value("b", false)
     .value("c", true)
  .end()
.done();

-> {"a":[1,2],"b":false,"c":true}

Writing to a stream or writer is very similar:

JsonWriter.on(httpResponse.getWriter())
  .array()
     .value(false)
     .value(true)
  .end()
.done();

You can also quickly convert a JsonArray, a JsonObject, or any JSON primitive to a string:

JsonArray array = ...
String json = JsonWriter.string(array);

If you attempt to write invalid JSON, JsonWriter will throw a runtime JsonWriterException.

JSON types

nanojson provides two helper types for dealing with JSON objects and arrays: JsonObject and JsonArray. These are subclasses of HashMap and ArrayList, and add helper methods to cast the underlying type of the member to one of the given JSON primitives.

These helper types also provide a builder that can be used in the same way as a JsonWriter:

JsonArray a = JsonArray.builder()
    .value(1)
    .value(2)
    .object()
        .value("abc": 123)
    .end()
.done();

Compliance

Release steps

  • Ensure that ~/.m2/settings.xml is correctly configured with username/password for sonatype-nexus-staging
  • Update the version in the pom.xml from -SNAPSHOT to
  • GPG_TTY=$(tty) mvn -Prelease clean deploy
  • Update README.md with new release version and pom.xml with new -SNAPSHOT version

nanojson's People

Contributors

chenzhang22 avatar dependabot-preview[bot] avatar dependabot[bot] avatar drplantabyte avatar dweiss avatar jstuyts avatar mmastrac avatar tillerino avatar tobigr avatar yeregorix avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nanojson's Issues

Writing of lazy numbers

I was looking for a json parser/formatter which can format JSON without normalizing numbers. It could be useful for test frameworks where we want to format json to make it human readable but with a minimum of other changes. For example, trailing decimal places would be preserved.

I thought maybe nanojson with lazy numbers could do that.

	@Test
	public void testTrailingDecimalLazy() throws JsonParserException {
		Object value = JsonParser.any().withLazyNumbers().from("1.000");
		String json = JsonWriter.string().value(value).done();
		assertEquals("1.000", json);
	}

Unfortunately, lazy numbers do not work with JsonWriter. Rather than "1.000", I get "com.grack.nanojson.JsonLazyNumber@18bf3d14". Seems toString() method is missing in JsonLazyNumber.

Is this intentional or accidental? If accidental, can it be added?

Illegal Unicode Escaping

This is kind of a "gotcha" type issue but I think my reasoning is sound.
nanojson parses Java character literals correctly (and goes to great
lengths to make sure the UTF-8 is valid) in the consumeTokenStringUtf8Char
method. But when the characters are escaped (not Java character literals)
it uses the following simplified logic:

int escaped = 0;

for (int i = 0; i < 4; i++) {
    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);

which is not sufficient because there are illegal combinations of encoded
Unicode characters (as well as illegal single characters) which I will now attempt
to show/describe.

The JSON spec states that any character may be escaped. The method
to escape characters outside of the Basic Multilingual Plane (U+0000
through U+FFFF) is as follows:

`To escape an extended character that is not in the Basic Multilingual
Plane, the character is represented as a 12-character sequence,
encoding the UTF-16 surrogate pair. So, for example, a string
containing only the G clef character (U+1D11E) may be represented as
"\uD834\uDD1E".

In other words, characters above U+FFFF are encoded as surrogate pairs.
Thus, the following JSON: "\uD800\uDC00" should decode to the Unicode
character: U+10000.

The following JSON Strings:

1.) "\uD800"
2.) "\uD800\uCC00"

Should fail parsing because a high surrogate character (U+D800) is:

in 1.) not followed by another Unicode character
in 2.) not followed by a low surrogate character

In other words, the following tests should not fail:

@Test
public void testFailBustedString8() {
    try {
        // High-surrogate character not followed by another Unicode character
        JsonParser.any().from("\"\\uD800\"");
        fail();
    } catch (JsonParserException e) {
        testException(e, 1, 7); // 7 may be the wrong char, but is irrelevant for the issue
    }
}

@Test
public void testFailBustedString9() {
    try {
        // High-surrogate character not followed by a low-surrogate character
        JsonParser.any().from("\"\\uD800\\uCC00\"");
        fail();
    } catch (JsonParserException e) {
        testException(e, 1, 7); // 7 may be the wrong char, but is irrelevant for the issue
    }
}

Thanks for your time, let me know if anything is not clear.

split got me throw a JsonWriterException: Unclosed JSON objects and/or arrays when closing writer

public static String getSteemitRequest(String method, Object[] params) {
        JsonStringWriter steemitRequestsWriter = JsonWriter.string().object();
        steemitRequestsWriter.value(Words.ID, getId());
        steemitRequestsWriter.value(Words.JSONRPC, Versions.SECOND_ONE_DOT);
        steemitRequestsWriter.value(Words.METHOD, method);
        steemitRequestsWriter.value(Words.PARAMS, params);
        return steemitRequestsWriter.done();
}
...
getSteemitRequest("get_content", "@sempervideo/mq3ykiml".split("/"));

got me this Exception:

com.grack.nanojson.JsonWriterException: Unclosed JSON objects and/or arrays when closing writer
	at com.grack.nanojson.JsonWriterBase.doneInternal(Unknown Source)
	at com.grack.nanojson.JsonStringWriter.done(Unknown Source)

Why ?

Cut a 1.1 release

The 1.1 release needs to be pushed out to Maven. We also need to submit a patch to JSONTestSuite to use 1.1.

Custom UTF8 stream decoding overhead/gain

Just FYI. The custom handling of UTF8 in JsonTokener is impressive but made me wonder how much is actually there to gain (compared to a buffered reader over a decoding stream). I made a quick wall-time repeated check parsing a contiguous json (with majority in plain ascii but containing some more complex multibyte sequences). The input file is ~350MB and fits entirely in I/O caches.

After a small JVM warmup of a few rounds, the results stabilized around:

internal utf8-decoder: ~1.57 seconds
JDK's utf8-decoder (reader): ~2.00 seconds

So the gain is there but really tiny considering that you typically also do something to the result (and this dominates raw parsing).

Build failure due to failed unit tests.

I tried to build your nanojson project (downloaded master on 2014-05-27) using the command on your README page ("mvn clean compile test jar:jar"). However, it failed some of the unit tests and halted the build.

My build computer is Windows 8.1 (x64) with JDK8 (x64, build 5) and Apache Maven 3.2.1

Below is the output of running "mvn clean compile test jar:jar" :
[INFO] Scanning for projects...
[INFO]
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building nanojson 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ nanojson ---
[INFO] Deleting C:\Users\CCHall\Downloads\nanojson-master\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ nanojson ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\CCHall\Downloads\nanojson-master\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ nanojson ---
[INFO] Compiling 13 source files to C:\Users\CCHall\Downloads\nanojson-master\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ nanojson ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\CCHall\Downloads\nanojson-master\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ nanojson ---
[INFO] Compiling 1 source file to C:\Users\CCHall\Downloads\nanojson-master\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ nanojson ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 3 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ nanojson ---
[INFO] Compiling 5 source files to C:\Users\CCHall\Downloads\nanojson-master\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ nanojson ---
[INFO] Surefire report directory: C:\Users\CCHall\Downloads\nanojson-master\target\surefire-reports


T E S T S

Running com.grack.nanojson.JsonNumberTest
Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.093 sec
Running com.grack.nanojson.JsonParserTest
Tests run: 62, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 5.194 sec <<< FAILURE!
testObjectWithEverything(com.grack.nanojson.JsonParserTest) Time elapsed: 0.007 sec <<< FAILURE!
org.junit.ComparisonFailure: expected:<{[jkl=null, abc=123, ghi=[true, false], def=456.0], mno=true}> but was:<{[abc=123, def=456.0, ghi=[true, false], jkl=null], mno=true}>
at org.junit.Assert.assertEquals(Assert.java:124)
at org.junit.Assert.assertEquals(Assert.java:146)
at com.grack.nanojson.JsonParserTest.testObjectWithEverything(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testObjectTwoElements(com.grack.nanojson.JsonParserTest) Time elapsed: 0.002 sec <<< FAILURE!
org.junit.ComparisonFailure: expected:<{[B=1, a]=1}> but was:<{[a=1, B]=1}>
at org.junit.Assert.assertEquals(Assert.java:124)
at org.junit.Assert.assertEquals(Assert.java:146)
at com.grack.nanojson.JsonParserTest.testObjectTwoElements(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

Running com.grack.nanojson.JsonTypesTest
Tests run: 12, Failures: 2, Errors: 0, Skipped: 0, Time elapsed: 0.01 sec <<< FAILURE!
testJsonArrayBuilder(com.grack.nanojson.JsonTypesTest) Time elapsed: 0 sec <<< FAILURE!
org.junit.ComparisonFailure: expected:<...,[1,null],[1,2,3],{"[b":null,"c":null,"a]":null}]> but was:<...,[1,null],[1,2,3],{"[a":null,"b":null,"c]":null}]>
at org.junit.Assert.assertEquals(Assert.java:124)
at org.junit.Assert.assertEquals(Assert.java:146)
at com.grack.nanojson.JsonTypesTest.testJsonArrayBuilder(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

testJsonObjectBuilder(com.grack.nanojson.JsonTypesTest) Time elapsed: 0.003 sec <<< FAILURE!
org.junit.ComparisonFailure: expected:<{"b[igint":1234567890,"int":1,"string":"hi","existingObject":{"b":null,"c":null,"a":null},"existingArray":[1,2,3],"object":{"abc":123},"bool":true,"double":1.0,"float":1.0,"null":null,"array":[1,null]]}> but was:<{"b[ool":true,"string":"hi","null":null,"existingArray":[1,2,3],"array":[1,null],"double":1.0,"float":1.0,"existingObject":{"a":null,"b":null,"c":null},"bigint":1234567890,"int":1,"object":{"abc":123}]}>
at org.junit.Assert.assertEquals(Assert.java:124)
at org.junit.Assert.assertEquals(Assert.java:146)
at com.grack.nanojson.JsonTypesTest.testJsonObjectBuilder(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

Running com.grack.nanojson.JsonWriterTest
Tests run: 35, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.065 sec <<< FAILURE!
testObjectArrayInMap(com.grack.nanojson.JsonWriterTest) Time elapsed: 0 sec <<< FAILURE!
org.junit.ComparisonFailure: expected:<{"array of [JsonObject":[{},null],"array of Boolean":[true,false]],"array of string":...> but was:<{"array of [Boolean":[true,false],"array of JsonObject":[{},null]],"array of string":...>
at org.junit.Assert.assertEquals(Assert.java:124)
at org.junit.Assert.assertEquals(Assert.java:146)
at com.grack.nanojson.JsonWriterTest.testObjectArrayInMap(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:44)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

Results :

Failed tests: testObjectWithEverything(com.grack.nanojson.JsonParserTest): expected:<{[jkl=null, abc=123, ghi=[true, false], def=456.0], mno=true}> but was:<{[abc=123, def=456.0, ghi=[true, false], jkl=null], mno=true}>
testObjectTwoElements(com.grack.nanojson.JsonParserTest): expected:<{[B=1, a]=1}> but was:<{[a=1, B]=1}>
testJsonArrayBuilder(com.grack.nanojson.JsonTypesTest): expected:<...,[1,null],[1,2,3],{"[b":null,"c":null,"a]":null}]> but was:<...,[1,null],[1,2,3],{"[a":null,"b":null,"c]":null}]>
testJsonObjectBuilder(com.grack.nanojson.JsonTypesTest): expected:<{"b[igint":1234567890,"int":1,"string":"hi","existingObject":{"b":null,"c":null,"a":null},"existingArray":[1,2,3],"object":{"abc":123},"bool":true,"double":1.0,"float":1.0,"null":null,"array":[1,null]]}> but was:<{"b[ool":true,"string":"hi","null":null,"existingArray":[1,2,3],"array":[1,null],"double":1.0,"float":1.0,"existingObject":{"a":null,"b":null,"c":null},"bigint":1234567890,"int":1,"object":{"abc":123}]}>
testObjectArrayInMap(com.grack.nanojson.JsonWriterTest): expected:<{"array of [JsonObject":[{},null],"array of Boolean":[true,false]],"array of string":...> but was:<{"array of [Boolean":[true,false],"array of JsonObject":[{},null]],"array of string":...>

Tests run: 119, Failures: 5, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.045 s
[INFO] Finished at: 2014-05-27T15:12:12-05:00
[INFO] Final Memory: 25M/92M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project nanojson: There are test failures.
[ERROR]
[ERROR] Please refer to C:\Users\CCHall\Downloads\nanojson-master\target\surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

com.grack.nanojson.JsonLazyNumber should implement toString() like other java.lang.Number classes

Hello,

Unlike other java.lang.Number classes, com.grack.nanojson.JsonLazyNumber does not implement toString(), which poses a problem when serializing back to JSON a Java object obtained by parsing its JSON form.

Example: parse "[1,2.0,3.14]" as a JsonArray and serialize it back using JsonWriter.string(Object) and you'll get something like "[com.grack.nanojson.JsonLazyNumber@HHH,com.grack.nanojson.JsonLazyNumber@HHH,com.grack.nanojson.JsonLazyNumber@HHH]".

This is the only problem I found after using nanojson for more than a year. I like it very much: tiny, stable, reliable, documented, no dependencies. We currently use it in XMLmind XML Editor, a commercial product.

Thank you very much for your great software component!

PS: A non-essential feature request. JsonWriter.string(Object) and its likes could support generating indented strings. Not for production use of course, but useful when testing and debugging.

Bump minimum Java to Java 8?

This would result in a number of simplified code paths (where you could use getOrDefault, standard charsets, etc.). I'm even tempted to say switch directly to Java 11 as the minimum but since it's such a compact, self-contained project Java 8 seems like a reasonable switch?

non-finite numbers are written as invalid JSON

So it appears that non-finite numbers produce invalid JSON, as far as I can tell. For example, the following round trip crashes:

String json = JsonWriter.string().value(Double.NaN).done();
JsonReader.from(json).doubleVal();

The reason is that the produced JSON is simply NaN. I think it should be "NaN" - this is at least what Jackson does. Either that, or the parser should be able to read unquoted non-finite values.

Not a blocker for me, but I thought you would want to know.

nanojson could use a more active maintainer

I haven't had much time to work on the library recently, but I've merged my JsonReader branch in and the codebase is in good shape. If anyone is interested in taking over, please let me know.

Make keys preserve ordering in JsonObject

Great project, @mmastrac - thank you for sharing it. I eyeballed the code and have a few minor nitpicks that I can file as a PR if you're interested. One major change that I made locally is to make JsonObject extend LinkedHashMap instead of HashMap. This allows deserialized objects to retain key ordering which is sometimes useful. What do you think?

Stack overflow error caused by nanojson parsing of untrusted JSON String

Stack overflow error caused by nanojson parsing of untrusted JSON String

Description

Using nanojson to parse untrusted JSON String may be vulnerable to denial of service (DOS) attacks. If the parser is running on user supplied input, an attacker may supply content that causes the parser to crash by stackoverflow.

Error Log

Exception in thread "main" java.lang.StackOverflowError
	at java.base/java.lang.AbstractStringBuilder.setLength(AbstractStringBuilder.java:274)
	at java.base/java.lang.StringBuilder.setLength(StringBuilder.java:90)
	at com.grack.nanojson.JsonTokener.consumeTokenString(Unknown Source)
	at com.grack.nanojson.JsonTokener.advanceToToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)
	at com.grack.nanojson.JsonParser.advanceToken(Unknown Source)

PoC

        <dependency>
            <groupId>com.grack</groupId>
            <artifactId>nanojson</artifactId>
            <version>1.8</version>
        </dependency>
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;

/**
 * JsonFuzzer
 *
 * @since 1.0.0
 */
public class JsonFuzzer {
    public final static int TOO_DEEP_NESTING = 9999;
    public final static String TOO_DEEP_Array = _nestedDoc(TOO_DEEP_NESTING, "[ ", "] ", "1");
    public final static String TOO_DEEP_Object = "{" + _nestedDoc(TOO_DEEP_NESTING, "\"a\": { ", "} ", "1") + "}";

    public static String _nestedDoc(int nesting, String open, String close, String content) {
        StringBuilder sb = new StringBuilder(nesting * (open.length() + close.length()));
        for (int i = 0; i < nesting; ++i) {
            sb.append(open);
            if ((i & 31) == 0) {
                sb.append("\n");
            }
        }
        sb.append("\n").append(content).append("\n");
        for (int i = 0; i < nesting; ++i) {
            sb.append(close);
            if ((i & 31) == 0) {
                sb.append("\n");
            }
        }
        return sb.toString();
    }
    public static void main(String[] args) {
        try {
            JsonParser.object().from(TOO_DEEP_Object);
            JsonParser.array().from(TOO_DEEP_Array);
        } catch (JsonParserException e) {
            throw new RuntimeException(e);
        }
    }
}

Rectification Solution

  1. Refer to the solution of jackson-databind: Add the depth variable to record the current parsing depth. If the parsing depth exceeds a certain threshold, an exception is thrown. (FasterXML/jackson-databind@fcfc499)

  2. Refer to the GSON solution: Change the recursive processing on deeply nested arrays or JSON objects to stack+iteration processing.((google/gson@2d01d6a20f39881c692977564c1ea591d9f39027))

Expose JsonWriterBase#preValue(java.lang.String)

Hi there,

would you be open to somehow exposing the preValue(java.lang.String) method of JsonWriterBase? The context is the following:

Suppose that you wanted to split up serialization of your objects into multiple methods that can be reused. Let's say you have classes A and B:

class A {
  B b;
  // ...
}
class B {
  int c;
}

If I wanted to extract serialization of B into its own method, I'd have to write something like:

void writeA(A a, JsonSink sink) {
  sink.object();
  sink.object("b");
  writeB(sink);
  sink.end();
  sink.end();
}
void writeB(B b, JsonSink sink) {
  sink.value("c", b.c);
}

I need to provide a value when specifying a field name, so I need to write the object marker of B outside of writeB. This means that writeB cannot be used as a standalone method. What would fix that is exposing the preValue method, e.g. through a field method:

void writeA(A a, JsonSink sink) {
  sink.object()
  sink.field("b");
  writeB(sink);
  sink.end();
}
void writeB(B b, JsonSink sink) {
  sink.object()
  sink.value("c", b.c)
  sink.end();
}

Cheers

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.