templates.docs.jsonObjects.html Maven / Gradle / Ivy
Show all versions of spincast-website Show documentation
{#==========================================
Docs : "JsonObjects"
==========================================#}
JsonObjects
JsonObject
(and JsonArray
)
are components provided by Spincast to mimic real Json
objects.
You can think of JsonObjects
as Map<String, Object>
on steroids!
JsonObjects
provide methods to get elements from them in a typed manner
They also support JsonPaths, which is an easy
way to navigate to a particular element in the JsonObject
.
JsonObjects
are also very easy to
validate.
Here's a quick example of using a JsonObject
:
// Creates a new JsonObject
JsonObject jsonObj = getJsonManager().create();
// Adds an element
jsonObj.set("myElement", "42");
// Gets the element as a String
String asString = jsonObj.getString("myElement");
// Gets the same element as an Integer
Integer asInteger = jsonObj.getInteger("myElement");
JsonObject
supports those types, natively :
-
String
-
Integer
-
Long
-
Float
-
Double
-
BigDecimal
-
Boolean
-
byte[] (serialized as a base 64 encoded String)
-
Date
-
Instant
-
Other
JsonObjects
and JsonArrays
Getters are provided for all of those native types :
-
String getString(String key)
-
Integer getInteger(String key)
-
JsonObject getJsonObject(String key)
-
...
Every Getter has an overloaded version that you can use to provide a default value
in case
the requested element if not found (by default, null
is returned if the element is not found).
Let's see an example :
// Creates an empty JsonObject
JsonObject jsonObj = getJsonManager().create();
// Tries to get an inexistent element...
// "myElement" will be NULL
String myElement = jsonObj.getString("nope");
// Tries to get an inexistent element, but also specifies a default value...
// "myElement" will be "myDefaultValue"!
String myElement = jsonObj.getString("nope", "myDefaultValue");
When you add an object of a type that is not managed natively, the object
is automatically converted
to a JsonObject
(or to a JsonArray
,
if the source is an array
or a Collection
). Spincast does this
by using the
SpincastJsonManager#convertToNativeType(...)
method, which is based on Jackson by default.
For example :
// Gets a typed user
User user = getUser(123);
// Adds this user to a JsonObject
JsonObject jsonObj = getJsonManager().create();
jsonObj.set("myUser", user);
// Gets back the user... It is now a JsonObject!
JsonObject userAsJsonObj = jsonObj.getJsonObject("myUser");
Note that you can have control over how an object is converted to a JsonObject
by implementing
the ToJsonObjectConvertible
interface. This interface contains a convertToJsonObject()
method that you implement to
convert your object to a JsonObject
the way you want. There is a similar
ToJsonArrayConvertible
interface to control how an object is converted to a JsonArray
.
Creating a JsonObject
You can create an JsonObject
(or an JsonArray
) by using the JsonManager
component. This JsonManager
can be injected where you want, or it can be accessed through the
json()
add-on, when you are inside a Route Handler
:
public void myHandler(AppRequestContext context) {
// JsonObject creation
JsonObject obj = context.json().create();
obj.set("name", "Stromgol");
obj.set("lastName", "Laroche");
// JsonArray creation
JsonArray array = context.json().createArray();
array.add(111);
array.add(222);
// Or, using the JsonManager directly (if
// injected in the current class) :
JsonObject obj2 = getJsonManager().create();
//...
}
Cloning and Immutability
By default, any JsonObject
(or JsonArray
) added to another JsonObject
is added as is, without
being cloned. This means that any external modification to the added element
will affect the element inside the JsonObject
, and vice-versa, since they
both refere to the same instance. This allows you to do something like :
JsonArray colors = getJsonManager().createArray();
JsonObject obj = getJsonManager().create();
// Adds the array to the obj
obj.set("colors", colors);
// Only then do we add elements to the array
colors.add("red");
colors.add("blue");
// This returns "red" : the array inside the JsonObject
// is the same instance as the external one.
String firstColor = obj.getArrayFirstString("colors");
Sometimes this behavior is not wanted, though. You may need the external object and the added object
to be two distinct instances so modifications to one don't affect the other!
In those cases, you can call the "clone()"
method on the original
JsonObject
object, or you can use "true"
as the "clone"
parameter when calling set(...)
/add(...)
methods.
Both methods result in the original object being cloned. Let's see an example :
JsonArray colors = getJsonManager().createArray();
JsonObject obj = getJsonManager().create();
// Add a *clone* of the array to the object
obj.set("colors", colors, true);
// Or :
obj.set("colors", colors.clone());
// Then we add elements to the original array
colors.add("red");
colors.add("blue");
// This will now return NULL since a *new* instance of the
// array has been added to the JsonObject!
String firstColor = obj.getArrayFirstString("colors");
Note that when you clone a JsonObject
, a deep copy of the original object is made,
which means the root object and all the children are cloned. The resulting JsonObject
is guaranteed to
share no element at all with the original object.
We also decided to make JsonObject
and JsonArray
objects
mutable by default. This is a
conscious decision to make those objects easy to work with : you can add and remove elements from them at any time.
But if you need more safety, if you work in a complex multi-threaded environment for example, you can
get an immutable version
of a JsonObject
object by calling its .clone(false)
method, using
false
as the "mutable"
parameter :
JsonObject obj = getJsonManager().create();
obj.set("name", "Stromgol");
// "false" => make the clone not mutable!
JsonObject immutableClone = obj.clone(false);
// This will throw an exception!
immutableClone.set("nope", "doesn't work");
When you create an immutable clones, the root element and all the children are cloned as immutable.
In fact, JsonObject
objects are always fully mutable or fully
immutable! Because of this, if you try to add an immutable JsonObject
to a mutable one,
a mutable clone will be created from the immutable object before being added. Same thing
if you try to add an mutable JsonObject
to an immutable one :
an immutable clone will be created from the mutable object before being added.
At runtime, you can validate if a JsonObject
is mutable or not using :
if(myJsonObj.isMutable())
.
JsonObject methods
Have a look at the JsonObject Javadoc
for a complete list of available methods. Here we're simply going to introduce some interesting ones,
other than set(...)
, getXXX(...)
and clone(...)
we already saw :
-
int size()
The number of properties on the object.
-
boolean contains(String jsonPath)
Does the JsonObject
contain an element at the specified
JsonPath?
-
JsonObject merge(JsonObject jsonObj, boolean clone)
JsonObject merge(Map<String, Object> map, boolean clone)
Merges a external JsonObject
or a plain Map<String, Object>
into the JsonObject
.
You can specify if the added elements must be cloned or
not (in case some are JsonObject
or JsonArray
).
-
JsonObject remove(String jsonPath)
Removes an element using its jsonPath
.
-
JsonObject getJsonObjectOrEmpty(String jsonPath)
JsonArray getJsonArrayOrEmpty(String jsonPath)
Returns the JsonObject
or JsonArray
at the specified
JsonPath
or returns an empty instance if it's not found.
This allows you to try to get a deep element without any potential NullPointerException
. For
example :
// This won't throw any NPE, even if the "myArrayKey"
// array or its first element don't exist
String value = obj.getJsonArrayOrEmpty("myArrayKey")
.getJsonObjectOrEmpty(0)
.getString("someKey", "defaultValue");
-
[TYPE] getArrayFirst[TYPE](String jsonPath, String defaultValue)
For all types native to JsonObject
, a getArrayFirst[TYPE](...)
method exists. With those methods, you can get the first element of a JsonArray
located at the specified JsonPath
. This is useful in situations where you
know the array only contains a single element :
// This :
String value = obj.getArrayFirstString("myArrayKey", "defaultValue")
// ... is the same as :
String value = obj.getJsonArrayOrEmpty("myArrayKey").getString(0, "defaultValue")
-
void transform(String jsonPath, ElementTransformer transformer)
Applies an ElementTransformer
to the element located at the specify JsonPath
. This is used to modify an element without
having to extract it first. For example, the provided JsonObject#trim(String jsonPath)
method exactly does this : it internally calls transform(...)
and pass it
an ElementTransformer
which trims the target element.
-
String toJsonString(boolean pretty)
Converts the JsonObject
object to a Json
string.
If pretty
is true, the resulting Json
will be formatted.
-
Map<String, Object> convertToPlainMap()
If you need to use the elements of a JsonObject
in some code that doesn't
know how to handle JsonObjects
, you can convert it to a plain Map<String, Object>
.
Spincast does this, for example, to pass the elements of the
response model to the Template Engine
when it's time to evaluate a template and send an HTML
page.
Pebble, the default templating Engine
, knows nothing about
JsonObjects
but can easily deal with a plain Map<String, Object>
.
Note that all JsonObject
children will be converted to
Map<String, Object>
too, and all JsonArray
children to
List<Object>
.
JsonArray extra methods
Have a look at the JsonArray Javadoc
for a complete list of available methods. Here are some interesting ones, other than the
ones also available on JsonObjects
:
-
List<Object> convertToPlainList()
Converts the JsonArray
to a plain List<Object>
.
All JsonObject
children will be converted to
Map<String, Object>
, and all JsonArray
children to
List<Object>
.
-
List<String> convertToStringList()
Converts the JsonArray
to a List<String>
.
To achieve this, the toString()
method will be called on any non null
element of the array.
JsonPaths
In Spincast, a JsonPath
is a simple way of expressing the location
of an element inside a JsonObject
(or a JsonArray
).
For example :
String title = myJsonObj.getString("user.books[3].title");
In this example, "user.books[3].title"
is
a JsonPath
targetting the title of the fourth
book from a user element of the myJsonObj
object.
Without using a JsonPath
, you would need to write something like
that to retrieve the same title :
JsonObject user = myJsonObj.getJsonObjectOrEmpty("user");
JsonArray books = user.getJsonArrayOrEmpty("books");
JsonObject book = books.getJsonObjectOrEmpty(3);
String title = book.getString("title");
As you can see, a JSonPath
allows you to easily navigate a JsonObject
without having to extract any intermediate elements.
Here is the syntax supported by JsonPaths
:
-
To access a child you use a
"."
.
For example : "user.car"
.
-
To access the element of a
JsonArray
you use "[X]"
, where X
is the position of the element in
the array (the first index is 0
).
For example : "books[3]"
.
-
If a key contains spaces, or a reserved character
(
"."
, "["
or "]"
), you need to surround it
with brackets. For example : "user['a key with spaces']"
.
-
That's it!
You can also use JsonPaths
to
insert elements at specific positions! For example :
// Creates a book object
JsonObject book = getJsonManager().create();
book.set("title", "The Hitchhiker's Guide to the Galaxy");
// Creates a "myJsonObj" object and adds the book to it
JsonObject myJsonObj = getJsonManager().create();
myJsonObj.set("user.books[2]", book);
The myJsonObj
object is now :
{
"user" : {
"books" : [
null,
null,
{
"title" : "The Hitchhiker's Guide to the Galaxy"
}
]
}
}
Notice that, in that example, the user
object
didn't exist when we inserted the book! When you add
an element using a JsonPath
, all the
parents are automatically created, if required.
If you really need to insert an element
in a JsonObject
using a key containing some of the JsonPaths
special characters (which are "."
, "["
and "]"
),
and without that key being parsed as a JsonPath
, you can do so by
using the setNoKeyParsing(...)
method. For example :
// Creates a book object
JsonObject book = getJsonManager().create();
book.set("title", "The Hitchhiker's Guide to the Galaxy");
// Creates a "myJsonObj" object and adds the book to it
// using an unparsed key!
JsonObject myJsonObj = getJsonManager().create();
myJsonObj.setNoKeyParsing("user.books[2]", book);
The myJsonObj
object is now :
{
"user.books[2]" : {
"title" : "The Hitchhiker's Guide to the Galaxy"
}
}
As you can see, the "user.books[2]"
key has been taken
as is, without being parsed as a JsonPath
.