This project, as of version 1.5, is dual licensed under both LGPLv3 and ASL 2.0. See file LICENSE for more details. Versions 1.0 and lower are licensed under LGPLv3 only.
This project uses Gradle as a build system. See file BUILD.md
for
details.
Credits where they are due: other people have contributed to this project, and this project would
not have reached its current state without them. Please refer to the CONTRIBUTORS.md
file in this
project for details.
This package is meant to be used with Jackson 2.2.x. It provides the three following features:
- write/read JSON decimal numbers using
BigDecimal
(instead ofdouble
) for optimal numeric precision; - (since 1.6) preconfigured JSON reader with trailing data detection support (see below);
- JSON numeric equivalence;
- JSON Pointer support.
jackson-coreutils
is 2.0jackson-coreutils-equivalence
is 1.0
Javadoc is available online.
File RELEASE-NOTES.md
has historical information.
With Gradle:
dependencies {
compile(group: "com.github.java-json-tools", name: "jackson-coreutils", version: "2.0");
compile(group: "com.github.java-json-tools", name: "jackson-coreutils-equivalence", version: "1.0");
}
With Maven:
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>jackson-coreutils</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.github.java-json-tools</groupId>
<artifactId>jackson-coreutils-equivalence</artifactId>
<version>1.0</version>
</dependency>
All classes in this package whose purpose is to load JSON data (JsonLoader
, Jackson mappers
provided by JacksonUtils
, and JsonLoader
since version 1.6) will deserialize all decimal
instances using BigDecimal
(more appropriately, Jackson's DecimalNode
). This allows to retain
the numeric value with full precision and not be a victim of IEEE 754's limitations in this regard.
Jackson's default JSON deserialization, when reading an input such as this:
[]]
will read the initial value ([]
) and stop there, ignoring the trailing ]
. This behaviour can be
beneficial in the event that you have a "streaming" JSON source, however it is not suitable if you
know you have only one JSON value to read and want to report an error if trailing data is detected.
This package provides a JsonNodeReader
class which will fail with an exception on trailing input.
When reading JSON into a JsonNode
, Jackson will serialize 1
as an IntNode
but 1.0
as a
DoubleNode
(or a DecimalNode
).
Understandably so, Jackson will not consider such nodes to be equal, since they are not of the same class. But, understandably so as well, some uses of JSON out there, including JSON Schema and JSON Patch's test operation, want to consider such nodes as equal.
This package provides an implementation of Guava's Equivalence
which considers that two numeric
JSON values are equal if their value is mathematically equal -- recursively so. That is, JSON values
1
and 1.0
will be considered equivalent; but so will be all possible JSON representations of
mathematical value 1 (including, for instance, 10e-1
). And evaluation is recursive, which means
that:
[ 1, 2, 3 ]
will be considered equivalent to:
[ 10e-1, 2.0, 0.3e1 ]
As of v2.0, the Guava Equivalence
is in the jackson-coreutils-equivalence
subproject so that
jackson-coreutils
remains Guava-free.
JSON Pointer is an IETF RFC (6901) which allows to unambiguously address any value into a JSON document (including the document itself, with the empty pointer). It is used in several IETF drafts and/or RFCs:
- JSON Reference (as the fragment part);
- JSON Patch.
The implementation in this package applies to all TreeNode
s as of Jackson 2.2.x; this includes
JsonNode
.
Several options are available:
// JsonLoader
final JsonNode node = JsonLoader.fromFile(new File("whatever.json"));
final JsonNode node = JsonLoader.fromResource("/in/class/path/my.json");
// Get a preconfigured ObjectMapper or reader with BigDecimal deserialization
final ObjectMapper mapper = JacksonUtils.newMapper();
final ObjectReader reader = JacksonUtils.getReader();
// Get a JsonNodeReader; see below
final JsonNodeReader reader = new JsonNodeReader();
The class to use is JsonNodeReader
:
// Default reader, no additional options
final JsonNodeReader reader = new JsonNodeReader();
// Provide a custom ObjectMapper
final ObjectMapper mapper = ...;
final JsonNodeReader reader = new JsonNodeReader(mapper);
// Read from an InputStream, a Reader
final JsonNode node = reader.fromInputStream(...);
final JsonNode node = reader.fromReader(...);
Note that the JsonLoader
class uses a JsonNodeReader
.
Given two JsonNode
instances which you want to be equivalent if their JSON number values are the
same, you can use:
if (JsonNumEquals.getInstance().equivalent(node1, node2))
// do something
You can also use this package to add JsonNode
instances to a set:
final Equivalence<JsonNode> eq = JsonNumEquals.getInstance();
// Note: uses Guava's Sets to create the set
final Set<Equivalence.Wrapper<JsonNode>> set
= Sets.newHashSet();
// Insert values
set.add(eq.wrap(node1));
set.add(eq.wrap(node2));
// etc
There are several ways you can build one:
// Build from an input string -- potentially throws JsonPointerException on malformed inputs
final JsonPointer ptr = new JsonPointer("/foo/bar");
// Build from a series of raw tokens -- never throws an exception
final JsonPointer ptr = JsonPointer.of("foo", "bar", 1); // Yields pointer "/foo/bar/1"
// Get another pointer's parent:
final JsonPointer parent = ptr.parent();
Note that JsonPointer
is immutable:
// DON'T DO THAT: value of "ptr" will not change
ptr.append("foo");
// Do that instead
ptr = ptr.append("foo");
Then, to use it, use either the .get()
or the .path()
methods:
// "node" is a JsonNode
// .get() returns null if there is no such path
final JsonNode child = ptr.get(node);
// Test if a path exists with .path()
if (!ptr.path(node).isMissingNode())
// do something