blob: 2c12d4efa25214d0cfe3a63025ec19e1d6c22df9 [file] [log] [blame]
//////////////////////////////////////////
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
//////////////////////////////////////////
ifndef::core-semantics[]
:core-semantics: core-semantics.adoc
endif::[]
ifndef::core-domain-specific-languages[]
:core-domain-specific-languages: core-domain-specific-languages.adoc
endif::[]
= Processing JSON
Groovy comes with integrated support for converting between Groovy objects and JSON. The classes dedicated to
JSON serialisation and parsing are found in the `groovy.json` package.
[[json_jsonslurper]]
== JsonSlurper
`JsonSlurper` is a class that parses JSON text or reader content into Groovy data structures (objects) such as maps, lists and
primitive types like `Integer`, `Double`, `Boolean` and `String`.
The class comes with a bunch of overloaded `parse` methods plus some special methods such as `parseText`,
`parseFile` and others. For the next example we will use the `parseText` method. It parses a JSON `String` and recursively converts it to a
list or map of objects. The other `parse*` methods are similar in that they return a JSON `String` but for different parameter
types.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=parse_text,indent=0]
----
Notice the result is a plain map and can be handled like a normal Groovy object instance. `JsonSlurper` parses the
given JSON as defined by the https://ecma-international.org/publications-and-standards/standards/ecma-404/[ECMA-404 JSON Interchange Standard]
plus support for JavaScript comments and dates.
In addition to maps `JsonSlurper` supports JSON arrays which are converted to lists.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=parse_list,indent=0]
----
The JSON standard supports the following primitive data types: string, number, object, `true`, `false` and `null`. `JsonSlurper`
converts these JSON types into corresponding Groovy types.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=parse_number,indent=0]
----
As `JsonSlurper` is returning pure Groovy object instances without any special JSON classes in the back, its usage
is transparent. In fact, `JsonSlurper` results conform to GPath expressions. GPath is a powerful expression language
that is supported by multiple slurpers for different data formats (`XmlSlurper` for XML being one example).
[NOTE]
For more details please have a look at the section on <<{core-semantics}#gpath_expressions,GPath expressions>>.
The following table gives an overview of the JSON types and the corresponding Groovy data types:
[cols="1,3" options="header"]
|===
|JSON
|Groovy
|string
|`java.lang.String`
|number
|`java.lang.BigDecimal` or `java.lang.Integer`
|object
|`java.util.LinkedHashMap`
|array
|`java.util.ArrayList`
|true
|`true`
|false
|`false`
|null
|`null`
|date
|`java.util.Date` based on the `yyyy-MM-dd'T'HH:mm:ssZ` date format
|===
[NOTE]
Whenever a value in JSON is `null`, `JsonSlurper` supplements it with the Groovy `null` value. This is in contrast to other
JSON parsers that represent a `null` value with a library-provided singleton object.
=== Parser Variants
`JsonSlurper` comes with a couple of parser implementations. Each parser fits different requirements, it could well be that for certain
scenarios the `JsonSlurper` default parser is not the best bet for all situations. Here is an overview of the shipped parser implementations:
* The `JsonParserCharArray` parser basically takes a JSON string and operates on the underlying character array. During value
conversion it copies character sub-arrays (a mechanism known as "chopping") and operates on them.
* The `JsonFastParser` is a special variant of the `JsonParserCharArray` and is the fastest parser. However, it is not the
default parser for a reason. `JsonFastParser` is a so-called index-overlay parser. During parsing of the given JSON `String` it
tries as hard as possible to avoid creating new char arrays or `String` instances. It keeps pointers to
the underlying original character array only. In addition, it defers object creation as late as possible. If parsed maps are
put into long-term caches care must be taken as the map objects might not be created and still consist of pointer to the
original char buffer only. However, `JsonFastParser` comes with a special chop mode which dices up the char buffer
early to keep a small copy of the original buffer. Recommendation is to use the `JsonFastParser` for JSON buffers
under 2MB and keeping the long-term cache restriction in mind.
* The `JsonParserLax` is a special variant of the `JsonParserCharArray` parser. It has similar performance characteristics as `JsonFastParser`
but differs in that it isn't exclusively relying on the ECMA-404 JSON grammar. For example it allows for comments, no quote strings etc.
* The `JsonParserUsingCharacterSource` is a special parser for very large files. It uses a technique called "character windowing" to
parse large JSON files (large means files over 2MB size in this case) with constant performance characteristics.
The default parser implementation for `JsonSlurper` is `JsonParserCharArray`. The `JsonParserType` enumeration contains constants for
the parser implementations described above:
[cols="1,3" options="header"]
|===
|Implementation
|Constant
|`JsonParserCharArray`
|`JsonParserType#CHAR_BUFFER`
|`JsonFastParser`
|`JsonParserType#INDEX_OVERLAY`
|`JsonParserLax`
|`JsonParserType#LAX`
|`JsonParserUsingCharacterSource`
|`JsonParserType#CHARACTER_SOURCE`
|===
Changing the parser implementation is as easy as setting the `JsonParserType` with a call to `JsonSlurper#setType()`.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=set_type,indent=0]
----
== JsonOutput
`JsonOutput` is responsible for serialising Groovy objects into JSON strings. It can be seen as companion object to
<<#json_jsonslurper,JsonSlurper>>, being a JSON parser.
`JsonOutput` comes with overloaded, static `toJson` methods. Each `toJson` implementation takes a different parameter type.
The static methods can either be used directly or by importing the methods with a static import statement.
The result of a `toJson` call is a `String` containing the JSON code.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=json_output,indent=0]
----
`JsonOutput` does not only support primitive, maps or list data types to be serialized to JSON, it goes further and even
has support for serialising POGOs, that is, plain-old Groovy objects.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=json_output_pogo,indent=0]
----
=== Customizing Output
If you need control over the serialized output you can use a `JsonGenerator`. The `JsonGenerator.Options` builder
can be used to create a customized generator. One or more options can be set on this builder in order to alter
the resulting output. When you are done setting the options simply call the `build()` method in order to get a fully
configured instance that will generate output based on the options selected.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=json_output_generator,indent=0]
----
A closure can be used to transform a type. These closure converters are registered for a given type and will be
called any time that type or a subtype is encountered. The first parameter to the closure is an object matching the
type for which the converter is registered and this parameter is required. The closure may take an optional second
`String` parameter and this will be set to the key name if one is available.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=json_output_converter,indent=0]
----
==== Formatted Output
As we saw in previous examples, the JSON output is not pretty printed per default. However, the `prettyPrint` method in `JsonOutput` comes
to rescue for this task.
[source,groovy]
----
include::../test/json/JsonTest.groovy[tags=pretty_print,indent=0]
----
`prettyPrint` takes a `String` as single parameter; therefore, it can be applied on arbitrary JSON `String` instances, not only the result of
`JsonOutput.toJson`.
=== Builders
Another way to create JSON from Groovy is to use `JsonBuilder` or `StreamingJsonBuilder`. Both builders provide a
DSL which allows to formulate an object graph which is then converted to JSON.
[NOTE]
For more details on builders, have a look at the builders chapter which covers both <<{core-domain-specific-languages}#_jsonbuilder,JsonBuilder>>
and <<{core-domain-specific-languages}#_streamingjsonbuilder,StreamingJsonBuilder>>.