June 17, 2013

MOXy's @XmlVariableNode - JSON Schema Example

We are in the process of adding the ability to generate a JSON Schema from your domain model to EclipseLink MOXy.  To accomplish this we have created a new Variable Node mapping. In this post I will demonstrate the new mapping by mapping a Java model to a JSON Schema.

You can try this out today using a nightly build of EclipseLink 2.6.0:

JSON Schema (input.json/Output) 

Below is the "Basic Example" taken from http://json-schema.org/examples.html.  Note how the type has many properties, but they don't appear as a JSON array.  Instead they appear as separate JSON objects keyed on the property name.

{
    "title": "Example Schema",
    "type": "object",
    "properties": {
        "firstName": {
            "type": "string"
        },
        "lastName": {
            "type": "string"
        },
        "age": {
            "description": "Age in years",
            "type": "integer",
            "minimum": 0
        }
    },
    "required": ["firstName", "lastName"]
}

Java Model

Below is the Java model we will use for this example.

JsonSchema (Properties Stored in a List)

In this Java representation of the JSON Schema we have a class that has a collection of Property objects.  Instead of the default representation of the collection (see:  Binding to JSON & XML - Handling Collections), we want each Property to be keyed by its name.  We can do this using the @XmlVariableNode annotation.  With it we specify the field/property from the target object that should be used as the key.

package blog.variablenode.jsonschema;

import java.util.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlVariableNode;

@XmlAccessorType(XmlAccessType.FIELD)
public class JsonSchema {

    private String title;
    
    private String type;
    
    @XmlElementWrapper
    @XmlVariableNode("name")
    public List<Property> properties;

    private List<String> required;

}

JsonSchema (Properties Stored in a Map)

In this version of the JsonSchema class we have changed the type of properties property from List<Property> property to Map<String, Property>.  The annotation remains the same, the difference is that when @XmlVariableNode is used on a Map the variable node name is used as the map key.

package blog.variablenode.jsonschema;

import java.util.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlVariableNode;

@XmlAccessorType(XmlAccessType.FIELD)
public class JsonSchema {

    private String title;
    
    private String type;
    
    @XmlElementWrapper
    @XmlVariableNode("name")
    public Map<String, Property> properties;

    private List<String> required;

}

Property

To prevent the name field from being marshalled we need to annotate it with @XmlTransient (see JAXB and Unmapped Properties).

package blog.variablenode.jsonschema;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Property {
    
    @XmlTransient
    private String name;

    private String description;
    
    private String type;
    
    private Integer minimum;

}

Demo Code

Below is some sample code that you can use to prove that everything works.

package blog.variablenode.jsonschema;

import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>();
        properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
        properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
        JAXBContext jc = JAXBContext.newInstance(new Class[] {JsonSchema.class}, properties);
        
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        StreamSource json = new StreamSource("src/blog/variablenode/jsonschema/input.json");
        JsonSchema jsonSchema = unmarshaller.unmarshal(json, JsonSchema.class).getValue();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(jsonSchema, System.out);
    }

}

External Metadata

MOXy also offers an external mapping document which allows you to provide metadata for third party objects or apply alternate mappings for your model (see: Mapping Object to Multiple XML Schemas - Weather Example).  Below is the mapping document for this example.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="blog.variablenode.jsonschema"
    xml-accessor-type="FIELD">
    <java-types>
        <java-type name="JsonSchema">
            <java-attributes>
                <xml-variable-node 
                    java-attribute="properties" java-variable-attribute="name">
                    <xml-element-wrapper/>
                </xml-variable-node>
            </java-attributes>
        </java-type>
        <java-type name="Property">
            <java-attributes>
                <xml-transient java-attribute="name"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Further Reading

If you enjoyed this post then you may also be interested in: 

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.