Documentation

Introduction

This page provides an example-based guide to Argo. In-depth details of the API are available in the online javadoc, which can also be found in the standard jar.

Model of JSON in Argo

JSON is defined in RFC 7159. There is a good explanation of the structure of JSON at http://www.json.org/. Argo models JSON using five types of entity - two that can have children:

and three types of leaf entity:

Producing JSON

There are two steps to producing JSON using Argo: First, an instance of JsonNode representing the JSON is built, and then an instance of JsonFormatter is used to turn that into a String representation.

A number of factory methods are provided for constructing JsonNodes in the JsonNodeFactories class:

import static argo.jdom.JsonNodeFactories.*; ... JsonNode json = object( field("name", string("Black Lace")), field("sales", number("110921")), field("totalRoyalties", number("10223.82")), field("singles", array( string("Superman"), string("Agadoo") )) );

All implementations of JsonNode are immutable. The same JsonNode could have been built up in a mutable manner using the methods in the JsonNodeBuilders class:

import static argo.jdom.JsonNodeBuilders.*; ... JsonObjectNodeBuilder builder = anObjectBuilder() .withField("name", aStringBuilder("Black Lace")) .withField("sales", aNumberBuilder("110921")) .withField("totalRoyalties", aNumberBuilder("10223.82")) .withField("singles", anArrayBuilder() .withElement(aStringBuilder("Superman")) .withElement(aStringBuilder("Agadoo")) ); JsonNode json = builder.build();

Finally, an instance of JsonFormatter is needed to produce JSON text from the JsonNode. The two implementations provided are PrettyJsonFormatter, which produces easy to read JSON, and CompactJsonFormatter, which produces very brief JSON. Instances of both classes can safely be shared between threads.

private static final JsonFormatter JSON_FORMATTER = new PrettyJsonFormatter(); ... String jsonText = JSON_FORMATTER.format(json);

Both classes also accept a Writer as an argument to the format method, to allow the output to be streamed.

Parsing JSON into an Object

All the examples in this section are based on the following JSON, which is assumed to be available in a String variable called jsonText.

{ "name": "Black Lace", "sales": 110921, "totalRoyalties": 10223.82, "singles": [ "Superman", "Agadoo" ] }

The JdomParser class is used to generate a JsonNode from JSON text. Instances of JdomParser can safely be shared between threads.

private static final JdomParser JDOM_PARSER = new JdomParser(); ... JsonNode json = JDOM_PARSER.parse(jsonText);

Instances of JsonNode are immutable. JsonNode provides methods for exploring the generated structure, as specified in the javadoc. The simplest of these are the getXXXValue(Object... pathElements) methods, which are used as follows:

String secondSingle = json.getStringValue("singles", 1);

It is also possible to check that the node at a particular path is of the required type as follows:

boolean isString = json.isStringValue("singles", 1);

Alternatively, JsonNodeSelectors can be used to extract data from the JsonNode in a typesafe manner. JsonNodeSelectors are functions that can be applied to JsonNodes to extract a value or a child JsonNode. The JsonNodeSelectors class provides a number of factory methods for constructing instances of JsonNodeSelector. The following code gets the name of the second single (note that like arrays, the index of the first element is zero):

private static final JsonNodeSelector<JsonNode,String> SECOND_SINGLE = JsonNodeSelectors.aStringNode("singles", 1); ... String secondSingle = SECOND_SINGLE.getValue(json);

The singles can also be converted into a List<String>:

private static final JsonNodeSelector<JsonNode, List<JsonNode>> SINGLES = anArrayNode("singles"); private static final JsonNodeSelector<JsonNode, String> SINGLE_NAME = aStringNode(); ... List<String> singles = new AbstractList<String>() { public String get(int index) { return SINGLE_NAME.getValue(SINGLES.getValue(json).get(index)); } public int size() { return SINGLES.getValue(json).size(); } };

JSON number handling deserves a brief explanation. JSON numbers are unlimited precision, and as such, cannot be represented by a Java primitive like double or int. The closest numeric representation offered by Java is BigDecimal, which accurately represents almost all JSON numbers, with the exception of some edge cases (for example, -0 and +0 are valid JSON numbers, but collapse to BigDecimal.ZERO). For this reason, Argo doesn't do any implicit conversion of JSON numbers - they're treated as Strings. However, helper methods are provided for converting these Strings to other Java numeric types. For example, you could retrieve the total royalties from the example JSON as follows:

import static argo.format.JsonNumberUtils.asBigDecimal; ... BigDecimal totalRoyalties = asBigDecimal(json.getNumberValue("totalRoyalties"));

Parsing JSON into events

The SajParser class offers a SAX style interface for parsing JSON. Given a Readerof a stream of JSON text, and an implementation of JsonListener, the parse method will parse the JSON from the Reader, and call methods on the JsonListener as it encounters parse events, such as the start of the document, the start of a field, or a JSON string.

The following code extracts the names of all the fields in a piece of JSON. It assumes jsonReader refers to a Reader of the example JSON in the previous section.

private static final SajParser SAJ_PARSER = new SajParser(); ... final Set<String> fieldNames = new HashSet<String>(); SAJ_PARSER.parse(jsonReader, new JsonListener() { public void startField(String name) { fieldNames.add(name); } public void startDocument() { } public void endDocument() { } ... });

Parsing JSON through iteration

The StajParser class allows the calling code to request parsing events from a stream of JSON text, similar to how the StAX parser works for XML.

The following code gets the names of all the fields in a piece of JSON using the StajParser. Again, it assumes jsonReader refers to a Reader of the example JSON in the previous section.

Set<String> fieldNames = new HashSet<String>(); StajParser stajParser = null; stajParser = new StajParser(jsonReader); while (stajParser.hasNext()) { JsonStreamElement next = stajParser.next(); if (next.jsonStreamElementType() == JsonStreamElementType.START_FIELD) { fieldNames.add(next.text()); } }

Iterating through the elements of a piece of JSON like this has the advantage that the parser only holds the current and next element in memory, so the above example would work for a piece of JSON larger than the available memory.