Argo is a free, open source JSON parsing and generating library for Java.

Argo is compliant with RFC 8259. It works with Java 5 onwards, and includes JPMS module information for Java 9 onwards. Argo has no dependencies. It’s licensed under the Apache 2 License.

The project is hosted on GitHub.

Downloads

Argo is published on Maven Central.

  • Gradle (Kotlin)

  • Gradle (Groovy)

  • Maven

dependencies {
    implementation(group = "net.sourceforge.argo", name = "argo", version = "7.6")
}
dependencies {
    implemenation group: 'net.sourceforge.argo', name: 'argo', version: '7.6'
}
<dependency>
    <groupId>net.sourceforge.argo</groupId>
    <artifactId>argo</artifactId>
    <version>7.6</version>
</dependency>

Usage

Let’s generate a JSON document to represent a blog entry.

String blogEntry = new JsonGenerator().generate(
        object( (1)
                field("title", string("How to use Argo")),
                field("version", number(1))
        )
);
1 Static factory methods in argo.jdom.JsonNodeFactories model JSON

Our blog entry JSON looks like this:

{
	"title": "How to use Argo",
	"version": 1
}

To retrieve the title from the blog entry JSON we produced, we parse it, and then extract the value of the title field.

String title = new JsonParser().parse(blogEntry).getStringValue("title");

Streaming

Suppose we want to add an array of comments retrieved from a data source to our blog entry JSON. To avoid reading all the comments into memory at once, and to begin outputting JSON text as early as possible, we can use streaming.

Iterable<String> comments = queryComments(); (1)
StringWriter stringWriter = new StringWriter(); (2)
new JsonGenerator().generate(stringWriter, (WriteableJsonObject) objectWriter -> {
    objectWriter.writeField("title", string("How to use Argo")); (3)
    objectWriter.writeField("version", number(2));
    objectWriter.writeField("comments", (WriteableJsonArray) arrayWriter -> {
        for (String comment : comments) { (4)
            arrayWriter.writeElement(string(comment));
        }
    });
});
1 Get a source of comments
2 A StringWriter for demonstration purposes, but could be any Writer
3 At this point, the StringWriter has already received:
{
	"title": "How to use Argo"
4 The Iterable of comments can be unbounded; our code only keeps one comment in memory at a time

To retrieve the comments from the JSON we just made without having to hold it all in memory, we can use streaming again.

int commentCount = 0;
StringReader stringReader = new StringReader(stringWriter.toString()); (1)
Deque<String> stack = new ArrayDeque<>(); (2)
Iterator<JsonStreamElement> jsonIterator = new JsonParser().parseStreaming(stringReader);
while (jsonIterator.hasNext()) {
    JsonStreamElement next = jsonIterator.next();
    switch (next.jsonStreamElementType()) {
        case START_FIELD:
            StringWriter fieldNameWriter = new StringWriter();
            try (Reader fieldNameReader = next.reader()) { (3)
                fieldNameReader.transferTo(fieldNameWriter);
            }
            stack.push(fieldNameWriter.toString());
            break;
        case END_FIELD:
            stack.pop();
            break;
        case STRING:
            if (List.of("comments").equals(List.copyOf(stack))) {
                commentCount++;
            }
            break;
        default:
            // ignore other element types
    }
}
1 A StringReader for demonstration purposes, but could be any Reader
2 The stack will keep track of the parents of the current element
3 Nodes' text is also streamed, so we can have unbounded strings, numbers, and field names

Performance tuning

Optional whitespace

The computational cost of parsing a JSON document correlates well with its length. Reducing or eliminating optional whitespace from input JSON reduces its size and results in a roughly proportional reduction in parsing cost.

Similarly, generating JSON with optional whitespace included incurs a processing cost. By default, Argo generates JSON with optional whitespace to improve readability. If performance is a greater concern than readability, optional whitespace can be switched off:

JsonGenerator jsonGenerator = new JsonGenerator().style(JsonGeneratorStyle.COMPACT);

Buffering

Argo does not add its own buffering to a Reader it parses from, or to a Writer it generates to. Parsing from an unbuffered Reader may be improved by wrapping it in an appropriately sized BufferedReader if the underlying Reader performs poorly when reading single characters and small arrays of characters. Likewise, generating JSON to an unbuffered Writer might be improved by wrapping it in a BufferedWriter if the underlying Writer is unsuited to single character and short String writes.

Position tracking

By default, Argo keeps track of the line and column it’s currently parsing, so that it can specify the position of syntax errors. Keeping track of position has a computational cost, but it can be switched off, in exchange for less informative error messages:

JsonParser jsonParser = new JsonParser().positionTracking(PositionTracking.DO_NOT_TRACK);

Node interning

When parsing a document without streaming, if Argo encounters a string or number equal to one it has previously encountered in the same document, by default it will use the same object for them. This strategy trades a reduction in memory for a small increase in computational cost. If the input is known to contain very little repetition, or if computational cost is prioritised over memory efficiency, it might be effective to disable object reuse:

JsonParser jsonParser = new JsonParser().nodeInterning(NodeInterningStrategy.INTERN_NOTHING);

Bear in mind that disabling object reuse will result in more object instantiation, which itself has a parse-time cost, and a garbage collection cost.

Further details

In-depth details of the API are available in the online javadoc.

Limitations

parse streaming parse generate streaming generate

Characters in a string

Integer.MAX_VALUE - 8

Unlimited

Integer.MAX_VALUE - 8

Unlimited

Characters in a number

Integer.MAX_VALUE - 8

Unlimited

Integer.MAX_VALUE - 8

Unlimited

Elements in an array

Integer.MAX_VALUE - 8

Unlimited

Integer.MAX_VALUE - 8

Unlimited

Fields in an object

Integer.MAX_VALUE - 8

Unlimited

Integer.MAX_VALUE - 8

Unlimited

Nesting depth

Integer.MAX_VALUE - 8

Integer.MAX_VALUE - 8

Limited by JVM stack (>2900 using 1MB stack)

Limited by JVM stack (>4000 using 1MB stack)