Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

EclipseLink/Development/404452

Planning: MOXy JSON Schema Generation

ER 404452

Currently EclipseLink supports generating XML Schemas from a JAXBContext. A feature will be added to generate JSON Schemas. At first this feature will be based on JAXB metadata and mappings, in the future this will be adapted to JSON-B metadata.

Overview

This feature will introduce one new public facing class, called JsonSchemaOutputResolver. This will allow the user to use existing, standard JAXB API to generate their schemas. By passing in an instance of JsonSchemaOutputResolver, EclipseLink will know to generate a Json Schema instead of an XML Schema.

JsonSchemaOutputResolver looks like this:

/**
 * PUBLIC:
 * <p><b>Purpose:</b>Provides a schema output resolver specifically for Json Schemas. By 
 * passing a subclass of JsonSchemaOutputResolver in to the JAXBContext generateSchema method, 
 * will indicate that a JsonSchema should be generated instead of an Xml Schema. 
 * 
 * @author mmacivor
 *
 */
public abstract class JsonSchemaOutputResolver extends SchemaOutputResolver {
 
    /**
     * Returns the root class of the schema to be generated. Json Schemas only have 1 root
     * level structure, so the class returned from this method will represent the root of the 
     * json schema. 
     */
    public abstract Class getRootClass();
 
}

Since, unlike an XML Schema, a Json schema can only have 1 root level construct, the JsonSchemaOutputResolver adds an additional method that return the Java Class that is to be the root of the generated schema.

Example of Generating a Json Schema

public class MyJsonOutputResolver extends JsonSchemaOutputResolver() {
 
    public Result createOutput(String namespaceURI, String suggestedFileName) throws IOException {
        return new StreamResult(System.out);
    }
 
    public Class getRootClass() {
        return Employee.class;
}
    public static void main(String[] args) {
        JAXBContext ctx = JAXBContext.newInstance("com.example.package");
        ctx.generateSchema(new MyJsonOutputResolver);
    }

Phases

The development of this feature will need to be split into multiple phases. The current EclipseLink JSON support is insufficient to map to a json schema document. Additional enhancements will be required in order to generate these schemas.

Phase 1: Additional Mapping Support * Completed *

JSON Schemas in several places specify collections of data where the name of the element corresponds to a property in the object. This is something that is not currently supported by eclipselink's oxm/json binding layer. This would require adding support

Example:

Mapping the list of properties on a schema. The name of the property is the key (or element) in the json schema, so this would require a variable-path mapping of some sort:

{
  ...
  {"properties": {
      "firstName":{"type":"string"},
      "lastName":{"type":"string"}
  }
}

Would need to be mapped to:

public class Schema {
  ...
  List<Property> properties;
}
public class Property {
  String name;
  String type;
}

Where name maps to the key/element name of the property entry.

More details on the new mapping described  here

Phase 2 - Basic Schema - *Completed*

The second phase would be to generate a basic schema for a single class with simple properties. This will require the creation of an object model to represent the schema and the code to process an EclipseLink project consisting of Descriptors and Mappings and generate the schema model for that project.

This could be done partially in parallel with Phase 1. The Schema Model and the code to create the Schema Model from the EclipseLink mappings could be done in advance. Actually mapping the Schema Model to output the json schema would require the completion of Phase 1.

Sample Object Model to be supported by Phase 2:

public class Employee {
  private int id;
  private String firstName;
  private String lastName;
  private List<String> responsibilities;
 
  public int getId() {
    return id;
  }
 
  public String getFirstName() {
    return firstName;
  }
 
  public String getLastName() {
    return lastName;
  }
 
  public List<String> getResponsibilities() {
    return responsibilities;
  }
 
  public void setId(int id) {
    this.id = id;
  }
 
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
 
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
 
  public void setResponsibilities(List<String> responsibilities) {
    this.responsibilities = responsibilities;
  }
}

Would generate the following Schema

{
  "title": "Employee",
  "type": "object",
  "properties": {
    "id": {
      "type":"integer"
    },
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "responsibilities": {
      "type":"array"
      "item": {
        "type":"string"
      }
    }
  }
  "required": ["id"]
}

Phase 3: More Complex Schemas *Completed*

Phase 3 would be to begin including more complex concepts into the schema model and generation code. This would include multiple types (in the "definitions" section of the schema) and references to those types. Also concepts like nested schemas and referencing external schemas could be added here.

Sample Object Model to be supported by Phase 3:

public class Employee {
  int id;
  String firstName;
  String lastName;
  List<String> responsibilities;
  Address address;
  List<PhoneNumber> phoneNumbers;
  Department department;
}
public Class Address {
 String street;
 String city;
 String country;
}
public Class PhoneNumber {
 String areaCode;
 String number;
}
public enumeration Department {
 DEVELOPMENT, SUPPORT, SALES, QA
}

Would generate the following Schema

{
  "title": "Employee",
  "type": "object",
  "properties": {
    "id": {
      "type":"integer"
    },
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "responsibilities": {
      "type":"array"
      "item": {
        "type":"string"
      }
    },
    "address": {
      "ref":"#/definitions/Address"
    },
    "phoneNumbers": {
      "type":"array",
      "items": {
        "ref":"#/definitions/PhoneNumber"
      }      
    },
    "department": {
      "ref":"#/definitions/Department"
    }
  },
  "required": ["id"]
  "definitions" : {
    "Address": {
      "type":"object",
      "properties": {
        "street": {
          "type":"string"
        },
        "city": {
          "type":"string"
        },
        "country": {
          "type":"string"
        }
      }
    },
    "PhoneNumber":{
      "type":"object",
      "properties": {
        "areaCode": {
          "type":"string"
        },
        "number": {
          "type":"string"
        }
      }
    },
    "Department":{
      "enum":["dev", "support", "sales", "qa"]
    }
}

Phase 4 - Additional Mappings and Properties - *Completed*

Any additional mappings not supported by Phase III will be added during this phase. Also, there are some Json Specific marshal properties which can have an effect on the generation of the schema. These will be handled by this phase of the development.

JSON Marshaller Properties:

  • JSON_ATTRIBUTE_PREFIX - This property sets a prefix to be prepending to attributes. In a JSON Schema, any properties that are generated from fields mapped with @XmlAttribute should have this prefix prepended.
  • JSON_INCLUDE_ROOT - This property indicates that a root specified by @XmlRootElement should be included in the marshalled json document. If this is set to true, then the generated json schema will also have to contain the root .
  • JSON_REDUCE_ANY_ARRAYS - This property won't effect the generation of the schema
  • JSON_MARSHAL_EMPTY_COLLECTIONS - This property only effects marshalling and won't have an effect on the schema.
  • JSON_VALUE_WRAPPER - This specifies a property name to be used for anything mapped with an XmlValue annotation. This will be included in the schema for DirectMappings with the xpath "text()"

</div>


Testing *In Progress*

JSON schema generation tests will be added to various test suites in the existing EclipseLink MOXy Test framework. The following is a list of test packages/suites that currently have a json schema generation test:

  • org.eclipse.persistence.testing.jaxb.json.attribute.JSONAttributePrefixOnContextTestSuite
  • org.eclipse.persistence.testing.jaxb.json.namespaces.NamespacesOnContextTestCases
  • org.eclipse.persistence.testing.jaxb.json.numbers.NumberFormatTestCases
  • org.eclipse.persistence.testing.jaxb.json.norootelement.IncludeRootFalseWithXMLRootElementTestCases
  • org.eclipse.persistence.testing.jaxb.json.xmlvalue.XMLValuePropTestCases
  • org.eclipse.persistence.testing.jaxb.annotations.xmlidref.self.XmlIdRefSelfTestCases
  • org.eclipse.persistence.testing.jaxb.annotations.xmlpath.XmlPathTestCases
  • org.eclipse.persistence.testing.jaxb.xmlelements.CircularTestCases
  • org.eclipse.persistence.testing.jaxb.xmlelements.XMLElementsArrayTestCases
  • org.eclipse.persistence.testing.jaxb.xmlelements.XmlElementsComplexTestCases
  • org.eclipse.persistence.testing.jaxb.annotations.xmlpath.self.SelfTestCases
  • org.eclipse.persistence.testing.jaxb.annotations.xmlcontaninerproperty.ContainterPropertyTestCases
  • org.eclipse.persistence.testing.jaxb.externalizedmetadata.mappings.binarydata.BinaryDataMappingTestCases
  • org.eclipse.persistence.testing.jaxb.externalizedmetadata.mappings.binarydatacollection.BinaryDataCollectionMappingTestCases
  • org.eclipse.persistence.testing.jaxb.xmlelementref.attachment.XMLElementRefTestCases
  • org.eclipse.persistence.testing.jaxb.inheritance.JAXBInheritanceTestCases
  • org.eclipse.persistence.testing.jaxb.xmlelements.XMLElementsInheritanceTestCases
  • org.eclipse.persistence.testing.jaxb.xmlelement.self.XmlElementSelfTestCases
  • org.eclipse.persistence.testing.jaxb.xmlidref.object.XmlIdRefObjectTestCases
  • org.eclipse.persistence.testing.jaxb.xmlidref.xmlelements.XmlElementsIdRefTestCases
  • org.eclipse.persistence.testing.jaxb.xmlvalue.XmlValueByteArrayTestCases
  • org.eclipse.persistence.testing.jaxb.annotations.xmlinversereference.InverseRefChoiceAdapterTestCases
  • org.eclipse.persistence.testing.jaxb.jaxbelement.simple.JAXBElementDataHandlerTestCases
  • org.eclipse.persistence.testing.jaxb.listofobjects.JAXBEmployeeArrayTestCases
  • org.eclipse.persistence.testing.jaxb.xmlidref.xmlelements.XmlElementsSingleIdRefTestCases
  • org.eclipse.persistence.testing.jaxb.xmlvalue.XmlValueByteArrayWithIdTestCases
  • org.eclipse.persistence.testing.jaxb.annotations.xmlvalue.text.NestTextTestCases


Known Issues/Bugs - *In Progress*

  • XmlVariableXPathObjectMapping/XmlVariableXPathCollectionMapping

Resolved Issues/Bugs

  • Bug 410638 CompositeMapping with no reference descriptor causes NPE
  • Cyclic references ie: Employee has a List<Employee> causes infinite loop
  • @XmlValue annotation (see org.eclipse.persistence.testing.jaxb.xmlidref.XmlIdRefTestCases)
  • BinaryData/BinaryDataCollectionMapping
  • XmlObjectReferenceMapping/XmlCollectionReferenceMapping
  • AnyObject/AnyCollectionMapping
  • Bug 410658 JSON_INCLUDE_ROOT property defaults to true, fixed, but may not be in time for 2.5.1
  • NAMESPACE_PREFIX_MAPPER & JSON_NAMESPACE_SEPARATOR - If both of these are set then namespace processing will be enabled, and property names will be prepended with the prefix corresponding to their namespace.
  • XmlInverseReferenceMapping (see org.eclipse.persistence.testing.jaxb.annotations.xmlcontainerproperty.ContainerPropertyTestCase)

Back to the top