Freemarker has some nice native support for working with JSON. And with the help of a few Liferay utility classes, it's very easy to even consume JSON web services from Freemarker. Note: you can view all of the example code on github: https://github.com/allen-ziegenfus/dev-playground/tree/master/freemarker
Converting JSON to Freemarker types
Freemarker hashes and sequences can be created from literal JSON strings as follows:
Creating a normal hash
<#-- Freemarker hash from JSON literal -->
<#
assign
beer_map = { "name": "Imperial Stout", "description": "Tasty Stout Beer"} >
<div>
${beer_map.name}
</div>
<div>
${beer_map.description}
</div>
Output:
Imperial Stout
Tasty Stout Beer
Creating a sequence
<#-- Freemarker array from JSON literal -->
<#
assign
food_pairing_array = ["Salmon", "Pizza with Taleggio"] >
<#
list
food_pairing_array as food_pairing>
<div>
${food_pairing}
</div>
</#
list>
Output:
Salmon
Pizza with Taleggio
Creating a complex hash with an embedded sequence
<#-- Freemarker hash with array from JSON literal -->
<#assign complex_beer_map =
{
"name": "Imperial Stout",
"description": "Tasty Stout Beer",
"food_pairing": [
"Salmon",
"Pizza with Taleggio"
]
} >
<div>
${complex_beer_map.name}
</div>
<div>
${complex_beer_map.description}
</div>
<#
list
complex_beer_map.food_pairing as food_pairing>
<div>
${food_pairing}
</div>
</#
list>
Output:
Imperial Stout
Tasty Stout Beer
Salmon
Pizza with Taleggio
But what if we have JSON in a String object? In this case there are a couple approaches:
Using the built-in ?eval
Using ?eval on a String that includes JSON will convert it like a literal to a Freemarker hash. As the name implies, Freemarker also evaluates any expressions in the String, which may or may not be what you want. For example, the reference ${name} is resolved, so the word "junk" appears in the output, even though it is not resolved through the assign tag (here I explicitly broke up the reference to show that ?eval is resolving it):
<h1>Simple deserialization with eval
</h1>
<#
assign
beer_json_string = "{ \"name\": \"Imperial Stout\", \"description\": \"Tasty Stout Beer\"}">
<#
assign
beer_map = beer_json_string?eval>
<div>
${beer_map.name}
</div>
<div>
${beer_map.description}
</div>
<h1> Simple deserialization with eval, resolving references
</h1>
<#
assign
name="junk">
<#
assign
beer_json_string_with_reference = "{ \"name\": \"Imperial Stout\", \"description\": \"Tasty " + "${" + "name} Stout Beer\"}">
<#
assign
beer_map_with_ref = beer_json_string_with_reference?eval>
<div>
${beer_map_with_ref.name}
</div>
<div>
${beer_map_with_ref.description}
</div>
Output:
Imperial Stout
Tasty Stout Beer
Imperial Stout
Tasty junk Stout Beer
Create a JSONObject with the Liferay jsonFactoryUtil
With the jsonFactoryUtil we can also pass a JSON string and get a JSONObject. This will not resolve any references (so no "junk"), but has the downside that we can't use expression language references with the resulting object. Instead we have to use the JSONObject methods like getString:
<h1>Deserialization with JSONObject
</h1>
<#
assign
liferay_beer_map = jsonFactoryUtil.createJSONObject(beer_json_string_with_reference)>
<div>
${liferay_beer_map.getString(
"name")
}
</div>
<div>
${liferay_beer_map.getString(
"description")
}
</div>
Output:
Imperial Stout
Tasty ${name} Stout Beer
Deserialization with jsonFactoryUtil
The deserialization methods of jsonFactoryUtil will return back an object that is apparently automagically mapped to a Freemarker hash. This makes referencing even nested JSON data easy and "natural", and you can also use the standard Freemarker operators to check for values or provide default values (!):
<h1>Deserialization with Freemarker Hash
</h1>
<#
assign
beer_hashmap = jsonFactoryUtil.looseDeserializeSafe(beer_json_string_with_reference) >
<div>
${beer_hashmap.name}
</div>
<div>
${beer_hashmap.description}
</div>
<div>
${(beer_hashmap.notthere)
!
}
</div>
<#
if
beer_hashmap.name??>
${beer_hashmap.name} exists!
</#
if>
Output:
Imperial Stout
Tasty ${name} Stout Beer
IImperial Stout exists!
Converting Freemarker Objects to JSON
To go the other direction we can also use our friend jsonFactoryUtil. In this case I instantiate a Freemarker hash with a literal, and then output the JSON to the browser console.
<#assign complex_beer_map =
{
"name": "Imperial Stout",
"description": "Tasty Stout Beer",
"food_pairing": [
"Salmon",
"Pizza with Taleggio"
]
} >
<script>
var
beerMap =
${jsonFactoryUtil.l
ooseSerializeDeep(
complex_beer_map)};
console.
log(
beerMap);
</script>

Bringing it all together
Finally, these techniques can be combined with using the httpUtil to pull JSON data from external services and create markup in Freemarker. Here is an example using the "Beer" api at https://punkapi.com/
<#
assign
response = httpUtil.URLtoString("http://api.punkapi.com/v2/beers/random")>
<#
assign
beers_info = jsonFactoryUtil.looseDeserializeSafe(response) >
<#
list
beers_info as beer_info>
<h1>
${beer_info.name}
</h1>
<div>
${beer_info.description}
</div>
<h2>Ingredients
</h2>
<#
list
beer_info.ingredients?keys as ingredient_type>
<h3>
${ingredient_type}
</h3>
<ul>
<#
if
beer_info.ingredients[ingredient_type]?is_sequence>
<#
list
beer_info.ingredients[ingredient_type] as ingredients>
<li>
${ingredients.name}
</li>
</#
list>
<#
else>
<li>
${beer_info.ingredients
[ingredient_type]
}
</li>
</#
if>
</ul>
</#
list>
</#
list>
This produces the following output!
Dogma
Brewed with over ten different types of malt, and blended together with Scottish heather honey, it is a pantheon to the gods of intricacy and nuance; a beer that celebratesa confluence of ideas. Complex, indulgent and encapsulating, Dogma gives more than a cursory nod to history, to make you ponder the very nature of beer itself.
Ingredients
yeast
- Wyeast 2007 - Pilsen Lager™
hops
malt
- Pale Ale
- Munich
- Caramalt
- Crystal
- Dark Crystal
- Wheat
- Flaked Oats
- Chocolate
- Smoked
- Amber
- Brown