This section contains the following tasks for converting objects to and from JSON documents.
Use the eclipselink.media-type property on your JAXB Marshaller or Unmarshaller to produce and use JSON documents with your application, as shown in Example 16-1.
Example 16-1 Marshalling and Unmarshalling
... Marshaller m = jaxbContext.createMarshaller(); m.setProperty("eclipselink.media-type", "application/json"); Unmarshaller u = jaxbContext.createUnmarshaller(); u.setProperty("eclipselink.media-type", "application/json"); ...
You can also specify the eclipselink.media-type
property in the Map
of the properties used when you create the JAXBContext
, as shown in Example 16-2.
Example 16-2 Using a Map
import org.eclipse.persistence.jaxb.JAXBContextProperties; import org.eclipse.persistence.oxm.MediaType; Map<String, Object> properties = new HashMap<String, Object>(); properties.put("eclipselink.media-type", "application/json"); JAXBContext ctx = JAXBContext.newInstance(new Class[] { Employee.class }, properties); Marshaller jsonMarshaller = ctx.createMarshaller(); Unmarshaller jsonUnmarshaller = ctx.createUnmarshaller();
When specified in a Map, the Marshallers and Unmarshallers created from the JAXBContent
will automatically use the specified media type.
You can also configure your application to use JSON documents by using the MarshallerProperties
, UnmarshallerProperties
, and MediaType
constants, as shown in Example 16-3.
Example 16-3 Using MarshallerProperties and UnarshallerProperties
import org.eclipse.persistence.jaxb.MarshallerProperties; import org.eclipse.persistence.jaxb.UnarshallerProperties; import org.eclipse.persistence.oxm.MediaType; m.setProperty(MarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON); u.setProperty(UnmarshallerProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON); ...
Example 16-4 shows a basic JSON binding that does not require compile time dependencies in addition to those required for normal JAXB usage. This example shows how to unmarshal JSON from a StreamSource
into the user object SearchResults
, add a new Result
to the collection, and then marshal the new collection to System.out
.
Example 16-4 Using Basic JSON Binding
package org.example; import org.example.model.Result; import org.example.model.SearchResults; import java.util.Date; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.transform.stream.StreamSource; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(SearchResults.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); unmarshaller.setProperty("eclipselink.media-type", "application/json"); StreamSource source = new StreamSource("http://search.twitter.com/search.json?q=jaxb"); JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class); Result result = new Result(); result.setCreatedAt(new Date()); result.setFromUser("bsmith"); result.setText("You can now use EclipseLink JAXB (MOXy) with JSON :)"); jaxbElement.getValue().getResults().add(result); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty("eclipselink.media-type", "application/json"); marshaller.marshal(jaxbElement, System.out); } }
You can also write MOXy External Bindings files as JSON documents. Example 16-5 shows how to use bindings.json
to map Customer and PhoneNumber classes to JSON.
Example 16-5 Using External Bindings
{ "package-name" : "org.example", "xml-schema" : { "element-form-default" : "QUALIFIED", "namespace" : "http://www.example.com/customer" }, "java-types" : { "java-type" : [ { "name" : "Customer", "xml-type" : { "prop-order" : "firstName lastName address phoneNumbers" }, "xml-root-element" : {}, "java-attributes" : { "xml-element" : [ {"java-attribute" : "firstName","name" : "first-name"}, {"java-attribute" : "lastName", "name" : "last-name"}, {"java-attribute" : "phoneNumbers","name" : "phone-number"} ] } }, { "name" : "PhoneNumber", "java-attributes" : { "xml-attribute" : [ {"java-attribute" : "type"} ], "xml-value" : [ {"java-attribute" : "number"} ] } } ] } }
Example 16-6 shows how to use the JSON file (created in Example 16-5) when bootstrapping a JAXBContext
.
Example 16-6 Using JSON to Bootstrap a JAXBContext
Map<String, Object> properties = new HashMap<String, Object>(2); properties.put("eclipselink.oxm.metadata-source", "org/example/binding.json"); properties.put("eclipselink.media-type", "application/json"); JAXBContext context = JAXBContext.newInstance("org.example", Customer.class.getClassLoader() , properties); Unmarshaller unmarshaller = context.createUnmarshaller(); StreamSource json = new StreamSource(new File("src/org/example/input.json")); ...
Although XML has a single data type, JSON differentiates between strings, numbers, and booleans. EclipseLink supports these data types automatically, as shown in Example 16-7
JSON does not use attributes; anything mapped with a @XmlAttribute
annotation will be marshalled as an element. By default, EclipseLink triggers both the attribute and element events, thereby allowing either the mapped attribute or element to handle the value.
You can override this behavior by using the JSON_ATTRIBUTE_PREFIX
property to specify an attribute prefix, as shown in Example 16-8. EclipseLink prepends the prefix to the attribute name during marshal and will recognize it during unmarshal.
In the example below the number
field is mapped as an attribute with the prefix @.
Example 16-8 Using a Prefix
jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@"); jsonMarshaller.setProperty(MarshallerProperties.JSON_ATTRIBUTE_PREFIX, "@") ;
{ "phone" : { "area-code" : "613", "@number" : "1234567" } }
You can also set the JSON_ATTRIBUTE_PREFIX
property in the Map used when creating the JAXBContext
, as shown in Example 16-9. All marshallers and unmarshallers created from the context will use the specified prefix.
EclipseLink supports JSON documents without a root element. By default, if no @XmlRootElement
annotation exists, the marshalled JSON document will not have a root element. You can override this behavior (that is omit the root element from the JSON output, even if the @XmlRootElement
is specified) by setting the JSON_INCLUDE_ROOT
property when marshalling a document, as shown in Example 16-10.
Example 16-10 Marshalling no Root Element Documents
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
When unmarshalling a document with no root elements, you should specify the class to which to unmarshal, as shown in Example 16-11.
Example 16-11 Unmarshalling no Root Element Documents
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false); JAXBElement<SearchResults> jaxbElement = unmarshaller.unmarshal(source, SearchResults.class);
Note: If the document has no root element, you must specify the class to unmarshal to. |
Because JSON does not use namespaces, by default all namespaces and prefixes are ignored when marshalling and unmarshalling. In some cases, this may be an issue if you have multiple mappings with the same local name – there will be no way to distinguish between the mappings.
With EclipseLink, you can supply a Map of namespace-to-prefix (or an instance of NamespacePrefixMapper
) to the Marshaller and Unmarshaller. The namespace prefix will appear in the marshalled document prepended to the element name. EclipseLink will recognize the prefix during an unmarshal operation and the resulting Java objects will be placed in the proper namespaces.
Example 16-12 shows how to use the NAMESPACE_PREFIX_MAPPER
property.
Example 16-12 Using Namesapces
Map<String, String> namespaces = new HashMap<String, String>(); namespaces.put("namespace1", "ns1"); namespaces.put("namespace2", "ns2"); jsonMarshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, namespaces); jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER, namespaces);
The MarshallerProperties.NAMESPACE_PREFIX_MAPPER
applies to both XML and JSON; UnmarshallerProperties.JSON_NAMESPACE_PREFIX_MAPPER
is a JSON-only property. XML unmarshalling can obtain the namespace information directly from the document.
When JSON is marshalled, the namespaces will be given the prefix from the Map separated by a dot ( . ):
{ "ns1.employee : { "ns2.id" : 123 } }
The dot separator can be set to any custom character by using the JSON_NAMESPACE_SEPARATOR
property. Here, a colon ( : ) will be used instead:
jsonMarshaller.setProperty(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':'); jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_NAMESPACE_SEPARATOR, ':');
By default, when marshalling to JSON, EclipseLink marshals empty collections as [ ]
, as shown in Example 16-13.
Use the JSON_MARSHAL_EMPTY_COLLECTIONS
property to override this behavior (so that empty collections are not marshalled at all).
jsonMarshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, Boolean.FALSE) ;
{ "phone" : { } }
If you use the @XmlRootElement(name="root")
annotation to specify a root level, the JSON document can be marshalled as:
marshaller.marshal(myListOfRoots, System.out);
[ { "root" : { "name" : "aaa" } }, { "root" : { "name" : "bbb" } } ]
Because the root element is present in the document, you can unmarsal it using:
unmarshaller.unmarshal(json);
If the class does not have an @XmlRootElement
(or if JSON_INCLUDE_ROOT
= false), the marshal would produce:
[ { "name":"aaa" }, { "name":"bbb" } ]
Because the root element is not present, you must indicate the class to unmarshal to:
unmarshaller.unmarshal(json, Root.class);
JAXB supports one or more @XmlAttributes
on @XmlValue classes
, as shown in Example 16-14.
Example 16-14 Using @XmlAttributes
public class Phone { @XmlValue public String number; @XmlAttribute public String areaCode; public Phone() { this("", ""); } public Phone(String num, String code) { this.number = num; this.areaCode = code; } }
To produce a valid JSON document, EclipseLink uses a value
wrapper, as shown in Example 16-15.
Example 16-15 Using a value Wrapper
{ "employee" : { "name" : "Bob Smith", "mainPhone" : { "areaCode" : "613", "value" : "555-5555" }, "otherPhones" : [ { "areaCode" : "613", "value" : "123-1234" }, { "areaCode" : "613", "value" : "345-3456" } ] } }
By default, EclipseLink uses value as the name of the wrapper. Use the JSON_VALUE_WRAPPER
property to customize the name of the value wrapper, as shown in Example 16-16.
Example 16-16 Customizing the Name of the Value Wrapper
jsonMarshaller.setProperty(MarshallerProperties.JSON_VALUE_WRAPPER, "$"); jsonUnmarshaller.setProperty(UnmarshallerProperties.JSON_VALUE_WRAPPER, "$");
Would produce:
{ "employee" : { "name" : "Bob Smith", "mainPhone" : { "areaCode" : "613", "$" : "555-5555" }, "otherPhones" : [ { "areaCode" : "613", "$" : "123-1234" }, { "areaCode" : "613", "$" : "345-3456" } ] } }
You can also specify the JSON_VALUE_WRAPPER
property in the Map
of the properties used when you create the JAXBContext
, as shown in Example 16-17.
Example 16-17 Using a Map
Map<String, Object> properties = new HashMap<String, Object>(); properties.put(JAXBContextProperties.JSON_VALUE_WRAPPER, "$"); JAXBContext ctx = JAXBContext.newInstance(new Class[] { Employee.class }, properties); Marshaller jsonMarshaller = ctx.createMarshaller(); Unmarshaller jsonUnmarshaller = ctx.createUnmarshaller();
When specified in a Map, the Marshallers and Unmarshallers created from the JAXBContent
will automatically use the specified value wrapper.