blob: a408bcc731df820d414076510d635f846db1974c [file] [log] [blame]
= GEP-7: JSON Support
:icons: font
.Metadata
****
[horizontal,options="compact"]
*Number*:: GEP-7
*Title*:: JSON Support
*Version*:: 4
*Type*:: Feature
*Status*:: Final
*Comment*:: Delivered in Groovy 1.8
*Leader*:: Guillaume Laforge
*Contributors*:: Andres Almiray
*Created*:: 2010-10-25
*Last modification* :: 2018-10-12
****
== Abstract: JSON Support
Provide a builder/slurper combination for handling data in JSON format in a similar fashion as it's already done for XML.
== Rationale
JSON has become ubiquitous to the web. RESTful services exchange data in both POX (Plain Old XML) and JSON formats.
Groovy has excellent support for producing/consuming XML with MarkupBuilder, XmlSlurper and XmlParser but lacks this
kind support for JSON. This GEP strives to remedy the situation, by providing a compatible builder approach.
=== Producing JSON
The following builder syntax is proposed
```
def builder = new groovy.json.JsonBuilder()
def root = builder.people {
person {
firstName 'Guillame'
lastName 'Laforge'
// Maps are valid values for objects too
address(
city: 'Paris',
country: 'France',
zip: 12345,
)
married true
conferences 'JavaOne', 'Gr8conf'
}
}
// creates a data structure made of maps (Json object) and lists (Json array)
assert root instanceof Map
println builder.toString()
// prints (without formatting)
{"people": {
"person": {
"firstName": "Guillaume",
"lastName": "Laforge",
"address": {
"city": "Paris",
"country": "France",
"zip": 12345
},
"married": true,
"conferences": [
"JavaOne",
"Gr8conf"
]
}
}
```
Valid node values are: `Number`, `String`, `GString`, `Boolean`, `Map`, `List`. `null` is reserved for object references.
Arrays can not be null but they can be empty. Anything else results in an IAE (or a more specialized exception) being thrown.
=== Special cases
There is a special case to be considered: when the top node results in an anonymous object or array.
or objects a call() method on the builder is needed which takes a map as argument, for arrays call() takes a vararg of values. Here are some examples:
```
builder.foo "foo"
// produces
{foo: "foo"}
```
```
builder([{
foo 'foo'
}])
// produces
[{"foo": "foo"}]
```
```
builder([[
foo: 'foo'
]])
// produces, same as above
[{"foo": "foo"}]
```
```
builder {
elem 1, 2, 3
}
// produces
{ "elem": [1, 2, 3] }
```
When a method is called on the builder without arguments, and empty JSON object is associated with the key:
```
builder.element()
// produces
{ "element": {} }
```
You can also pass a map and a closure argument:
```
builder.person(name: "Guillaume", age: 33) { town "Paris" }
// produces
{"name": "Guillaume", "age": 33, "town": "Paris}
```
Calls like the following, with a map and a value, don't have any meaningful representation in JSON (unlike in XML), and triggers a JsonException:
```
shouldFail(JsonException) {
builder.elem(a: 1, b: 2, "some text value")
}
```
In case of overlapping keys in the map and the closure, the closure wins a visual clue for this rule is that the closure appears "after" the map key/value pairs.
Consuming JSON
The proposal is for the creation of a JsonSlurper class that can read JSON from a string (in a non-streaming fashion) and produce a hierarchy of maps and lists representing the JSON objects and arrays respectively.
```
String json = '{"person": {"firstName": "Guillaume", "lastName": "Laforge", "conferences": ["JavaOne", "Gr8conf"]}}'
def root = new JsonSlurper().parseText(json)
assert root instanceof Map
assert root.person.conferences instanceof List
assert root.person.firstName == 'Guillaume'
assert root.person.conferences[1] == 'Gr8conf'
```
JsonSlurper's API should mirror closely what XmlParser/XmlSlurper offers in terms of its parse* method variants.
== References and useful links
JSON Spec and Java implementations
* http://json.org/[json.org]
* http://tools.ietf.org/html/rfc4627[RFC-4627]
* http://json-lib.sourceforge.net/[json-lib]
=== Mailing-list discussions
* https://markmail.org/thread/5ofqwr6t33okyh6g[groovy-dev: Built-in JSON support in 1.8]
=== JIRA issues
* https://issues.apache.org/jira/browse/GROOVY-4644[GROOVY-4644: JSON support: provide a parser and a builder for JSON content]
== Update history
3 (2011-02-02):: Version as extracted from Codehaus wiki
4 (2018-10-16):: Numerous minor tweaks