Skip to main content

juneau-bean-hal

The juneau-bean-hal module provides Java beans for the HAL (Hypertext Application Language) hypermedia format, also described in draft-kelly-json-hal-08. HAL is a JSON-based wire format that lets a REST API describe its resources alongside the links and embedded sub-resources that surround them.

Overview​

HAL standardises two reserved members on every resource: _links (a map of relation name to one or more HalLink objects) and _embedded (a map of relation name to one or more sub-resources). Everything else in the object is the resource's own payload. This module brings that wire format to Juneau as typed @Marshalled POJOs that round-trip through the default JsonSerializer / JsonParser — no custom marshaller required.

Key Features​

  • Typed beans for the full HAL spec — HalResource, HalLink, HalLinkArray, HalResourceArray, with all eight Link Object fields per draft-kelly-json-hal-08.
  • Spec-correct single-or-array handling — _links and _embedded map values can be either a single object or a JSON array per the spec. Per-property ObjectSwaps (HalLinkOrArraySwap / HalResourceOrArraySwap) handle the union both directions.
  • Open-ended payload via @BeanProp("*") — Arbitrary resource fields are added via set(String, Object) and read back via get(String) / extraKeys(). They serialise flat at the top level alongside _links and _embedded.
  • Fluent ergonomics for the single-or-array shapes — addLink(rel, link) sets a single-link entry; addLinks(rel, links...) promotes the entry to a HalLinkArray (creating one or extending an existing array as needed). Same pattern is mirrored for _embedded.
  • No external dependencies — Only depends on juneau-marshall.

Bean Classes​

ClassNotes
HalResourceTop-level HAL resource. Reserved _links and _embedded maps + open payload.
HalLinkLink Object with href, templated, type, deprecation, name, profile, title, hreflang.
HalLinkArrayLinkedList<HalLink> — multi-link branch inside an _links map.
HalResourceArrayLinkedList<HalResource> — multi-resource branch inside an _embedded map.
HalLinkOrArraySwapPer-property ObjectSwap resolving each _links value to single HalLink or HalLinkArray.
HalResourceOrArraySwapPer-property ObjectSwap resolving each _embedded value to single HalResource or HalResourceArray.

Basic Usage​

Building a HAL resource​

import org.apache.juneau.bean.hal.*;
import org.apache.juneau.marshall.json.*;

HalResource order = new HalResource()
.addLink("self", new HalLink("/orders/123"))
.addLink("warehouse", new HalLink("/warehouse/56"))
.addLink("invoice", new HalLink("/invoices/873"))
.addLinks("curies",
new HalLink("https://acme.example/{rel}").setName("acme").setTemplated(true))
.set("currency", "USD")
.set("status", "shipped")
.set("total", 30.00)
.addEmbedded("customer",
new HalResource()
.addLink("self", new HalLink("/customers/7"))
.set("name", "Kelsey"));

String wire = JsonSerializer.DEFAULT.write(order);
HalResource back = JsonParser.DEFAULT.read(wire, HalResource.class);

HAL allows a relation in _links to map to either a single Link Object or an array of them. The convenience methods make the choice explicit at the call site:

  • addLink(rel, link) — sets a single-link value at rel, replacing any existing value.
  • addLinks(rel, links...) — promotes the entry under rel to a HalLinkArray. If the existing value is a single HalLink, it is wrapped into a new array; if it is already an array, the new links are appended; if absent, a fresh array is created.
HalResource r = new HalResource()
.addLink("self", new HalLink("/orders/123")) // _links.self ⇒ object
.addLinks("curies",
new HalLink("https://acme.example/{rel}").setName("acme").setTemplated(true),
new HalLink("https://acme.example/v2/{rel}").setName("acme2").setTemplated(true)
); // _links.curies ⇒ array

The same pattern is mirrored on _embedded via addEmbedded(rel, res) and addEmbedded(rel, res...). The HalLinkOrArraySwap / HalResourceOrArraySwap swaps make both wire shapes round-trip cleanly through the default JSON parser.

Open payload via @BeanProp("*")​

Arbitrary resource fields are added via set(String, Object) and read back via get(String) / extraKeys(). They serialise flat into the top-level object, alongside (and not nested under) _links and _embedded.

HalResource r = new HalResource()
.addLink("self", new HalLink("/orders/123"))
.set("total", 99.50)
.set("currency", "USD");

// {"_links":{"self":{"href":"/orders/123"}},"total":99.5,"currency":"USD"}

_embedded is a tree, not a graph​

Per the spec, _embedded represents tree-shaped containment, not arbitrary graphs. Juneau's BeanTraverseContext throws on cyclic object references by default — which is the right behaviour here, so leave it on.

Content-Type​

The matching Content-Type constant lives in juneau-rest-common and has been available since 9.2.x:

import org.apache.juneau.http.header.ContentType;

ContentType.APPLICATION_HAL_JSON; // "application/hal+json"

Resources​