Team lead for the TopLink/EclipseLink JAXB (MOXy) & SDO implementations, and the Oracle representative on those specifications.
Team lead for the TopLink/EclipseLink JAXB (MOXy) & SDO implementations, and the Oracle representative on those specifications.
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>2.5.0</version>
</dependency>
import javax.xml.bind.JAXBElement;While useful JAXBElement can get in the way if you want to use your domain model with something like JPA (which doesn't know what to do with it). In this post I will demonstrate how you can eliminate the need for JAXBElement through the use of an XmlAdapter.
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlElementRefs({
@XmlElementRef(name = "billing-address"),
@XmlElementRef(name = "shipping-address")
})
private JAXBElement<Address> address;
}
Problem
The package name com.rccl.pcp.api.v1.pricetypes in the source you have given :
@javax.xml.bind.annotation.XmlSchema(namespace = "http://api.pcp.rccl.com/v1/priceTypes", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.rccl.pcp.api.v1.pricetypes;
does not match the package name com.rccl.services.v1.commontypes for the error.
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project ASRB2CServices: Compilation failure: Compilation failure: [ERROR] C:/cygwin/home/venitdun/projects/XForm_Maven_20130610-1/ASRB2CServices/src/main/java/com/rccl/services/v1/commontypes/package-info.java:[1,148] package annotations should be in file package-info.java
Solution
Either the contents of the package-info class are wrong, or it is in the wrong place.
Working Example
Below is a link to a JAXB example with a package-info class that is setup to compile/run with Maven
The @XmlElementRef annotation has the following requirements (see: http://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlElementRef.html):
If the collection item type (for collection property) or property type (for single valued property) is JAXBElement, then @XmlElementRef.name() and @XmlElementRef.namespace() must point an element factory method with an @XmlElementDecl annotation in a class annotated with @XmlRegistry (usually ObjectFactory class generated by the schema compiler) :
@XmlElementDecl.name() must equal @XmlElementRef.name()@XmlElementDecl.namespace() must equal @XmlElementRef.namespace().If the collection item type (for collection property) or property type (for single valued property) is not JAXBElement, then the type referenced by the property or field must be annotated with @XmlRootElement.
Since ListEntity will be processed as a class and not a type the data field will be treated as having type Object and therefore the requirements for @XmlElementRef will not have been met resulting in the exception that you are seeing.
I have confirmed the issue that you are seeing and have opened the following bug:
UPDATE
The fix for this issue has been checked into the EclipseLink 2.5.1 and 2.6.0 streams. You can get the fix in the corresponding nightly builds from the following link starting June 19, 2013:
You can use EclipseLink JAXB (MOXy)'s @XmlNamedObjectGraph extension to support this use case. What @XmlNamedObjectGraph allows you to do is create multiple views on your data.
Person
Below we will use @XmlNamedObjectGraph to create a view on the Person class that only exposes 2 fields (firstName and lastName).
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
@XmlNamedObjectGraph(
name = "simple",
attributeNodes = {
@XmlNamedAttributeNode("firstName"),
@XmlNamedAttributeNode("lastName")
}
)
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private int clientId;
private String firstName;
private String lastName;
private String email;
public void setClientId(int clientId) {
this.clientId = clientId;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setEmail(String email) {
this.email = email;
}
}
Policy
We will also use @XmlNamedObjectGraph on the Policy class. It says that for the userCreated field apply the named object graph called simple that we defined on the Person class.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlNamedObjectGraph(
name = "policy",
attributeNodes = {
@XmlNamedAttributeNode(value = "userCreated", subgraph = "simple"),
@XmlNamedAttributeNode("client")
}
)
public class Policy {
private Person userCreated;
private Person client;
public void setUserCreated(Person userCreated) {
this.userCreated = userCreated;
}
public void setClient(Person client) {
this.client = client;
}
}
Demo
In the demo code below we will specify the named object graph that we want applied on the Marshaller using the MarshallerProperties.OBJECT_GRAPH property.
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Policy.class);
Person person = new Person();
person.setClientId(1234);
person.setFirstName("John");
person.setLastName("Doe");
person.setEmail("jdoe@example.com");
Policy policy = new Policy();
policy.setClient(person);
policy.setUserCreated(person);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "policy");
marshaller.marshal(policy, System.out);
}
}
Output
Below is the output from running the demo code:
<?xml version="1.0" encoding="UTF-8"?>
<policy>
<userCreated>
<firstName>John</firstName>
<lastName>Doe</lastName>
</userCreated>
<client>
<clientId>1234</clientId>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>jdoe@example.com</email>
</client>
</policy>
For More Information
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You can use the @XmlPath extension in MOXy to flatten the XML structure.
Customer
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
@XmlPath(".")
private AccessRole accessRole;
}
AccessRole
import javax.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
public class AccessRole {
@XmlElement(name="accessRoleName")
private String name;
}
For More Information
A collection property can contain multiple types. With this use case if you want to be able to round trip the document you will need some sort of type indicator.
Option 1 - @XmlElements
The @XmlElements annotation takes an array of @XmlElement annotation. The purpose of this is to assign a unique element to identify each type.
Option 2 - @XmlElement(type=Object.class)
If you tell JAXB that the type is Object it will force each value to be written out with an xsi:type attribute to uniquely identify the type. Since the value types will not be auto discovered you will need to pass then on to create the JAXBContext or reference the with a @XmlSeeAlso annotation.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JST-222) expert group.
You could use MOXy's @XmlPath extension for this use case:
Response
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@XmlRootElement(name="root")
@XmlAccessorType(XmlAccessType.FIELD)
public class Response{
@XmlPath("record/AddressDetails/street/text()")
String street;
//getter and setters
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17141154/input.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<record>
<AddressDetails>
<street> M G Road </street>
</AddressDetails>
</record>
</root>
For More Information
JAXB Validation (JSR-222)
JAXB validation is based on an XML schema (see: http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html).
Bean Validation (JSR-303 & 349)
You could use Bean Validation 1.0 (JSR-303) or 1.1 (JSR-349) annotations on your model to define validation rules.
When the XJC tool is converting the XML schema(s) to Java classes it will automatically pull in imported/included schemas based on their system id. It those schemas are not available at the specified system id (or its not specified) then you could use an XML catalog.
For More Information
If you want to map to the middle of an XML document you could do the following:
Demo Code
You could use a StAX parser to advance to the part of the document you wish to unmarshal, and then unmarshal the XMLStreamReader from there,.
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/forum17141154/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
while(!(xsr.isStartElement() && xsr.getLocalName().equals("AddressDetails"))) {
xsr.next();
}
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Response> response = unmarshaller.unmarshal(xsr, Response.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
Response
The mappings on the domain object are made relative to the fragment of the XML document you are mapping to.
import javax.xml.bind.annotation.*;
@XmlAccessorType(XmlAccessType.FIELD)
public class Response{
String street;
//getter and setters
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<AddressDetails>
<street> M G Road </street>
</AddressDetails>
For More Information
XML schema allows you to define a collection type where the items are separated by a space.
<xs:list itemType="xs:int"/>
Below is a full example of how you could leverage this in JAXB to represent a matrix.
Java Model (Root)
We will use a 2 dimensional int array to represent out matrix. We will use an XmlAdapter to get a non-default array representation (see: JAXB & java.util.Map)
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Root {
private int[][] matrix;
@XmlJavaTypeAdapter(MatrixAdapter.class)
public int[][] getMatrix() {
return matrix;
}
public void setMatrix(int[][] matrix) {
this.matrix = matrix;
}
}
XmlAdapter (MatrixAdapter)
When you annotate int[] with @XmlValue the XML representation will be space separated text.
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MatrixAdapter extends XmlAdapter<MatrixAdapter.AdaptedMatrix, int[][]>{
public static class AdaptedMatrix {
@XmlElement(name="row")
public List<AdaptedRow> rows;
}
public static class AdaptedRow {
@XmlValue
public int[] row;
}
@Override
public AdaptedMatrix marshal(int[][] matrix) throws Exception {
AdaptedMatrix adaptedMatrix = new AdaptedMatrix();
adaptedMatrix.rows = new ArrayList<AdaptedRow>(matrix.length);
for(int[] row : matrix) {
AdaptedRow adaptedRow = new AdaptedRow();
adaptedRow.row = row;
adaptedMatrix.rows.add(adaptedRow);
}
return adaptedMatrix;
}
@Override
public int[][] unmarshal(AdaptedMatrix adaptedMatrix) throws Exception {
List<AdaptedRow> adaptedRows = adaptedMatrix.rows;
int[][] matrix = new int[adaptedRows.size()][];
for(int x=0; x<adaptedRows.size(); x++) {
matrix[x] = adaptedRows.get(x).row;
}
return matrix;
}
}
Demo Code
Below is some demo code you can run to prove that everything works:
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17119708/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<matrix>
<row>1 2 3 4</row>
<row>5 6 7 8</row>
</matrix>
</root>
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<matrix>
<row>1 2 3</row>
<row>4 5 6</row>
<row>7 8 9</row>
</matrix>
</root>
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
This functionality does not currently exist today, but it has been requested before:
There is also an open enhancement request where we are gather the requirements to add this type of feature to MOXy. I would be interested in getting your thoughts on it:
If less than half of the properties are mapped then you can specify @XmlAccessorType(XmlAccessType.NONE) on the class and then only fields/properties will JAXB Annotations will be mapped to XML.
If you want to configure the mappings by XML the EclipseLink MOXy implementation (I lead this project) offers a mapping file extension you can use.
When you create a JAXBContext from a Java class, the JAXB impl will also create metadata for all classes if can reach from it (including property types, super classes, and classes specified in annotations). It will not package scan or pull in subclasses so care needs to be taken to ensure metadata is produced for all classes in your model.
If your model is generated from an XML schema then you should create the JAXBContext from the generated package name. To ensure the classes are loaded correctly you should also specify the correct ClassLoader. Assuming your package name is com.example.foo you can do the following.
JAXBContext jc = JAXBContext.newInstance("com.example.foo", CommlAutoPolicyInfoType.class.getClassLoader());
If your generate model spans multiple packages you separate the packages in context path using the : character.
JAXBContext jc = JAXBContext.newInstance("com.example.foo:com.example.bar", CommlAutoPolicyInfoType.class.getClassLoader());
For More Information
When I run the following code using the JAXB implementation in JDK 1.7.0_21-b12 for the Mac with the documents from your question I get the enum value as output.
import javax.xml.XMLConstants;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
public class Demo {
public static void main(String args[]) throws Exception {
StreamSource xsd = new StreamSource("src/forum17114304/A.xsd");
StreamSource xml = new StreamSource("src/forum17114304/a.xml");
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(xsd);
schema.newValidator().validate(xml);
JAXBContext payloadContext = JAXBContext.newInstance("a");
Unmarshaller unmarshaller = payloadContext.createUnmarshaller();
unmarshaller.setSchema(schema);
JAXBElement<?> oUnmarshalled = (JAXBElement<?>) unmarshaller
.unmarshal(xml);
Object o = oUnmarshalled.getValue(); // returns ORGANISATION
System.out.println(o);
}
}
Output
ORGANISATION
UPDATE
There appears to be a bug in EclipseLink JAXB (MOXy) for this use case:
This bug has been fixed in the EclipseLink 2.5.1 and 2.6.0 streams. A nightly download can be obtained from the following link starting June 15, 2013:
This error often occurs when the JAXB annotation class is from a different ClassLoader than the ClassLoader the JAXB implement is using. If you are inside an OSGi enviroment make sure that you import the javax.xml.bind.annotation package in your MANIFEST.MF file.
You can use an external binding file resolve the property name conflicts. Below is an example of one I created to help someone generate classes from the DocBook version 5 XML Schema that you can use as a guide.
<jxb:bindings
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jxb:extensionBindingPrefixes="xjc"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:docbook="http://docbook.org/ns/docbook"
version="2.1">
<jxb:globalBindings>
<xjc:simple />
</jxb:globalBindings>
<jxb:bindings schemaLocation="docbook.xsd">
<jxb:bindings node="//xs:attributeGroup[@name='db.common.attributes']/xs:attribute[@name='version']">
<jxb:property name="commonVersion"/>
</jxb:bindings>
<jxb:bindings node="//xs:attributeGroup[@name='db.common.attributes']/xs:attribute[@name='revision']">
<jxb:property name="commonRevision"/>
</jxb:bindings>
<jxb:bindings node="//xs:attributeGroup[@name='db.common.attributes']/xs:attribute[@ref='xml:lang']">
<jxb:property name="xmlLang"/>
</jxb:bindings>
<jxb:bindings node="//xs:attributeGroup[@name='db.common.linking.attributes']/xs:attribute[@ref='xlink:role']">
<jxb:property name="xlinkRole"/>
</jxb:bindings>
<jxb:bindings node="//xs:attributeGroup[@name='db.common.linking.attributes']/xs:attribute[@ref='xlink:type']">
<jxb:property name="xlinkType"/>
</jxb:bindings>
<jxb:bindings node="//xs:attributeGroup[@name='db.common.linking.attributes']/xs:attribute[@ref='xlink:title']">
<jxb:property name="xlinkTitle"/>
</jxb:bindings>
<jxb:bindings node="//xs:element[@name='table']/xs:complexType/xs:attribute[@name='title']">
<jxb:property name="titleAttribute"/>
</jxb:bindings>
</jxb:bindings>
</jxb:bindings>
Full Example
I am assuming that whenever method X is executed , it writes the element into the file. This wont hold the converted XML until now in memory. Am I right?
The StAX parser may buffer a subsets of the XML in memory before writing it to a file to reduce the amount of disk I/O done to improve performance. It won't cache so much that you run out of memory.
Will this close the unclosed tags automatically like the first 2 start elements?
The writeEndDocument method will close any unclosed tags automatically.
Can I flush the writer and use the same writer again to append some more xml into the file like this:
You can keep using the same XMLStreamWriter to write content to an XML file until you call writeEndDocument. You don't need to explicitly call flush before you write more content. Once you are done with the XMLStreamWriter you should call close on it to free it up.
I ran your example using the XJC that comes with JDK 1.7.0_21-b12 for the Mac, and it worked fine. You should just need to switch not a newer version of XJC from the JAXB reference implmeentation (see: https://jaxb.java.net/) to get your use case to work.
For More Information
Your mappings look correct, below is a full example. Since you are annotating the field be sure you have @XmlAccessorType(XmlAccessType.FIELD) on your class (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).
Domain Model (Root)
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlPath("jaxbBean/@file")
List<String> jaxbBeansClasses;
}
jaxb.properties
To specify MOXy as your JAXB (JSR-222) provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17104179/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<jaxbBean file="A.groovy"/>
<jaxbBean file="B.groovy"/>
</root>
TL;DR
You can do a dummy marshal and leverage the JAXB validation mechanisms rather than using the javax.xml.validation mechanisms directly.
LEVERAGING Marshaller.Listener & ValidationEventHandler (CommodityValidator)
For this example we will leverage aspects of Marshaller.Listener and ValidationEventHandler to accomplish the use case.
Marshal.Listener - This will be called for each object being marshalled. We can use it to cache the instance of Order that we may need to remove the instance of Commodity from.ValidationEventHandler this will give us access to each problem occuring with validation during the marshal operation. For each problem it will be passed a ValidationEvent. This ValidationEvent will hold a ValidationEventLocator from which we can get the object that had the problem being marshalled.import javax.xml.bind.*;
public class CommodityValidator extends Marshaller.Listener implements ValidationEventHandler {
private Order order;
@Override
public void beforeMarshal(Object source) {
if(source instanceof Order) {
// If we are marshalling an Order Store It
order = (Order) source;
}
}
@Override
public boolean handleEvent(ValidationEvent event) {
if(event.getLocator().getObject() instanceof Commodity) {
// If the Error was Caused by a Commodity Object Remove it from the Order
order.setCommodity(null);
return true;
}
return false;
}
}
DEMO CODE
The following code can be run to prove that everything works.
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.bind.*;
import javax.xml.validation.*;
import org.xml.sax.helpers.DefaultHandler;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Orders.class);
// STEP 1 - Build the Object Model
Commodity commodity1 = new Commodity();
commodity1.setId("1");
Order order1 = new Order();
order1.setCommodity(commodity1);
Commodity commodityInvalid = new Commodity();
commodityInvalid.setId("INVALID");
Order order2 = new Order();
order2.setCommodity(commodityInvalid);
Commodity commodity3 = new Commodity();
commodity3.setId("3");
Order order3 = new Order();
order3.setCommodity(commodity3);
Orders orders = new Orders();
orders.getOrderList().add(order1);
orders.getOrderList().add(order2);
orders.getOrderList().add(order3);
// STEP 2 - Check that all the Commodities are Set
System.out.println("\nCommodities - Before Validation");
for(Order order : orders.getOrderList()) {
System.out.println(order.getCommodity());
}
// STEP 3 - Create the XML Schema
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("src/forum16953248/schema.xsd"));
// STEP 4 - Perform Validation with the Marshal Operation
Marshaller marshaller = jc.createMarshaller();
// STEP 4a - Set the Schema on the Marshaller
marshaller.setSchema(schema);
// STEP 4b - Set the CommodityValidator as the Listener and EventHandler
CommodityValidator commodityValidator = new CommodityValidator();
marshaller.setListener(commodityValidator);
marshaller.setEventHandler(commodityValidator);
// STEP 4c - Marshal to Anything
marshaller.marshal(orders, new DefaultHandler());
// STEP 5 - Check that the Invalid Commodity was Removed
System.out.println("\nCommodities - After Validation");
for(Order order : orders.getOrderList()) {
System.out.println(order.getCommodity());
}
}
}
OUTPUT
Below is the output from running the demo code. Node how after the marshal operation the invalid commodity was removed.
Commodities - Before Validation
forum16953248.Commodity@3bb505fe
forum16953248.Commodity@699c8551
forum16953248.Commodity@22f4bf02
Commodities - After Validation
forum16953248.Commodity@3bb505fe
null
forum16953248.Commodity@22f4bf02
XML SCHEMA (schema.xsd)
Below is the XML schema used for this example.
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="orders">
<complexType>
<sequence>
<element name="order" minOccurs="0" maxOccurs="unbounded">
<complexType>
<sequence>
<element name="commodity">
<complexType>
<attribute name="id" type="int"/>
</complexType>
</element>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>
JAVA MODEL
Below is the object model I used for this example.
Orders
import java.util.*;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Orders {
private List<Order> orderList = new ArrayList<Order>();
@XmlElement(name="order")
public List<Order> getOrderList() {
return orderList;
}
}
Order
public class Order {
private Commodity commodity;
public Commodity getCommodity() {
return commodity;
}
public void setCommodity(Commodity commodity) {
this.commodity = commodity;
}
}
Commodity
import javax.xml.bind.annotation.*;
public class Commodity {
private String id;
@XmlAttribute
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
You could use JAXB with StAX to do the following:
import java.util.*;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(User.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/forum17047306/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
List<User> users = new ArrayList<User>();
Unmarshaller unmarshaller = jc.createUnmarshaller();
while(xsr.getEventType() != XMLStreamReader.END_DOCUMENT) {
if(xsr.isStartElement() && "User".equals(xsr.getLocalName())) {
User user = (User) unmarshaller.unmarshal(xsr);
users.add(user);
}
xsr.next();
}
System.out.println(users.size());
}
}
UPDATE
You may prefer the following approach for handling lists using a generic list wrapper object:
What you are doing should work. Here is an example:
Domain Model (Foo)
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Foo {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo
import javax.xml.bind.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
public class Demo {
public static void main(String[] args) throws Exception {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(Foo.class);
// Create the Object
Foo foo = new Foo();
foo.setBar("Hello World");
// Create the Document
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.newDocument();
// Marshal the Object to a Document
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, document);
// Output the Document
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
t.transform(source, result);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="no"?><foo><bar>Hello World</bar></foo>
You could do the following:
httpExchange.sendResponseHeaders(rCode, 0);
OutputStream outputStream = httpExchange.getResonseBody();
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(myPojo, outputStream);
outputStream.close();
For this use case you should not use @XmlAnyElement(lax=true).
USE CASE #1 - @XmlElementRef
When using @XmlElementRef you need to ensure that the element name on the @XmlElementRef annotation matches the @XmlRootElement (or @XmlElementDecl) annotation exactly.
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<MyStuffData>
<BField/>
<CField/>
<BField/>
</MyStuffData>
</root>
Root
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlElementWrapper(name = "MyStuffData")
@XmlElementRefs({
@XmlElementRef(name = "BField", type = B.class),
@XmlElementRef(name = "CField", type = C.class),
})
private List<A> myStuff;
public List<A> getMyStuff() {
return myStuff;
}
}
USE CASE #2 - @XmlElements
If you do not wish to have the element name the same as the root element name for the reference type you can use the @XmlElements annotation.
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<MyStuffData>
<B/>
<C/>
<B/>
</MyStuffData>
</root>
Root
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlElementWrapper(name = "MyStuffData")
@XmlElements({
@XmlElement(name = "B", type = B.class),
@XmlElement(name = "C", type = C.class),
})
private List<A> myStuff;
public List<A> getMyStuff() {
return myStuff;
}
}
COMMON
The following are common to both use cases.
A
public abstract class A {
}
B
import javax.xml.bind.annotation.*;
@XmlRootElement(name = "BField")
public class B extends A {
}
C
import javax.xml.bind.annotation.*;
@XmlRootElement(name = "CField")
public class C extends A {
}
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17061559/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
for(A a : root.getMyStuff()) {
System.out.println(a);
}
}
}
Output
forum17061559.B@6998e5d9
forum17061559.C@351a3fb8
forum17061559.B@4e4d6
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"config"). Expected elements are (none)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:631)
You need to ensure that you associate a class with the root element of the XML document using @XmlRootElement or @XmlElementDecl (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html). Alternatively you can use one of the unmarshal methods that take a Class parameter to tell JAXB what type of object you are unmarshalling.
Domain Model (Config)
I would recommend having a domain class like the following from which you could obtain the two lists of Property objects.
import java.util.*;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Config {
private List<Property> logProperties = new ArrayList<Property>();
private List<Property> envProperties = new ArrayList<Property>();
@XmlElementWrapper(name="log")
@XmlElement(name="property")
public List<Property> getLogProperties() {
return logProperties;
}
@XmlElementWrapper(name="env")
@XmlElement(name="property")
public List<Property> getEnvProperties() {
return envProperties;
}
}
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Config.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17059227/input.xml");
Config config = (Config) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "file:///C:/Documents%20and%20Settings/mojalal/Desktop/FirstXSD.xml");
marshaller.marshal(config, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file:///C:/Documents%20and%20Settings/mojalal/Desktop/FirstXSD.xml">
<env>
<property key="firstenv" value="fo"/>
<property key="123" value="333"/>
</env>
<log>
<property key="firstKey" value="firstValue"/>
<property key="secoundKey" value="secoundKey"/>
<property key="thirdKey" value="thirdValue"/>
</log>
</config>
If you are (as per the jaxb tag) looking to do this with JAXB the steps are the following:
EclipseLink JAXB (MOXy)'s @XmlPath annotation supports a subset of the XPath specification. The XPath handling is done by MOXy itself. The following concepts are supported:
@idaddressaddress[1]address[@type='mailing']name/text()text().personal-info/name[2]/text()For namespace qualified nodes, the prefixes defined in the @XmlNs annotations can be used to qualify the XPath fragments. Unqualified fragments will assumed to be in the namespace specified using @XmlSchema.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
@XmlInverseReference is a EclipseLink JAXB (MOXy) extension that enables you to map bidirectional relationships:
@XmlInverseReference serves two roles:
Foo and Bar, it will marshal Foo then Bar and then it will stop before trying to marshal Foo again.Instead of adapting the Map to a List, you should adapt it to an object that has a List property.
XmlAdapter (MapPropertiesAdapter)
import java.util.*;
import java.util.Map.Entry;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MapPropertiesAdapter extends XmlAdapter<MapPropertiesAdapter.AdaptedProperties, Map<String, String>>{
public static class AdaptedProperties {
public List<Property> property = new ArrayList<Property>();
}
public static class Property {
@XmlAttribute
public String name;
@XmlValue
public String value;
}
@Override
public Map<String, String> unmarshal(AdaptedProperties adaptedProperties) throws Exception {
if(null == adaptedProperties) {
return null;
}
Map<String, String> map = new HashMap<String, String>(adaptedProperties.property.size());
for(Property property : adaptedProperties.property) {
map.put(property.name, property.value);
}
return map;
}
@Override
public AdaptedProperties marshal(Map<String, String> map) throws Exception {
if(null == map) {
return null;
}
AdaptedProperties adaptedProperties = new AdaptedProperties();
for(Entry<String,String> entry : map.entrySet()) {
Property property = new Property();
property.name = entry.getKey();
property.value = entry.getValue();
adaptedProperties.property.add(property);
}
return adaptedProperties;
}
}
Domain Model (Root)
Below is a model object with a Map property. The @XmlJavaTypeAdapter annotation is used to specify the XmlAdapter.
import java.util.Map;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlJavaTypeAdapter(MapPropertiesAdapter.class)
private Map<String, String> properties;
}
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17024050/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<properties>
<property name="A">a</property>
<property name="B">b</property>
</properties>
</root>
@user86834 - Something like?: blog.bdoughan.com/2012/11/…
The Person class has properties clientId, email, ` firstName`, lastName and based on where the object appears you want to restrict which properties are marshalled to XML?
@Imperative - I will put the fix in today and tomorrow you will be able to grab the June 19 nightly build. It is normally available in the morning eastern time.
@chad - That is where the Bean Validation JSRs come into play. It is a standard for specifying standalone validation rules.
@user2487308 - Specifying the package name should have worked for your use case (you need to specify the ClassLoader in an app server or OSGi environment. I have updated my answer to provide some additional info.
What type of environment are you running in (app server, Java SE, OSGi, etc)? How are you creating your JAXBContext?
@MarcusJuniusBrutus - A bug also existed in EclipseLink JAXB (MOXy) for this use case. I have fixed it in the EclipseLink 2.5.1 and 2.6.0 streams. The fix should be available in the nightly downloads starting tomorrow. If you are interested in trying out MOXy the following link will help you get started: blog.bdoughan.com/2011/05/…
What problem do you hit when you try to use JAXB with FpML?
@JSmith - It the document is namespace aware then you should make the XPath namespace aware. The fact that you see different behaviour when marshalling to DOMSource versus Document is evidence of this. If you swapped parser or JAXB providers you would probably see different results.
@LPD - I just checked the Javadoc and it says If the response length parameter is zero, then chunked transfer encoding is used and an arbitrary amount of data may be sent. I have updated my answer with the sendResponseHeader call.
aggregationType controls what is written out to XML, what is read in, or both?For this use case you could use @XmlElementWrapper to eliminate the need to the Log and Env classes (see: stackoverflow.com/a/17066435/383861).
+1 - @NishithShah JamesB is correct. When the document is not well formed there isn't much the JAXB implementation can do to recover an you are just going to get an exception.
Which information are you trying to get from the XML document? Are you only interested in the properties under the log element?
What do you want your Java model to look like? Do you also need to support writing the objects back to XML?
GlassFish 4 is now available offering the complete Java EE 7 (JSR-342) platform. EclipseLink made some major contributions to this...
On behalf of the MOXy JAXB committers (great job by all), I am very proud to announce that EclipseLink 2.5 has been released and is available for...
In a previous post I covered how EclipseLink JPA-RS can be used to expose a JPA persistence unit as a RESTful service. In that example we interacted...
In a previous series of posts I covered how to create a JAX-RS service that leveraged JPA for the persistence layer. EclipseLink contains a component...
Java will soon have a standard set of APIs for processing JSON as part of Java EE 7. This standard is being defined as JSR 353 - Java API for JSON...
Suppose you have a domain model that you want to expose as a RESTful service. The problem is you only want to input/output part of your data. ...
JSON with padding is a communication mechanism used in JavaScript to overcome restrictions due to the same origin policy (for more information see:...
One of EclipseLink JAXB (MOXy)'s strengths is the ability to map an object model to both JSON and XML with a single set of metadata. The one weakness...
Is it ironic that it can be difficult to map the java.util.Map class in JAXB (JSR-222)? In this post I will cover some items that will make it much...
In previous articles I demonstrated how EclipseLink JAXB (MOXy) is directly integrated into the JAX-WS implementations in WebLogic (as of 12.1.1) and in...
In this post I will cover how to differentiate between null and empty collections in the XML representation with JAXB (JSR-222). Demo Code The...
In a previous post I introduced how the @XmlAnyElement(lax=true) annotation could be used to create a very flexible mapping. In this post I'll...
JAXB (JSR-222)enables Java to treat a domain model as XML. In this post I will demonstrate how to leverage this by applying an XSLT stylesheet to a JAXB...
For some an XML schema is a strict set of rules for how the XML document must be structured. But for others it is a general guideline to...
JSON binding was added to EclipseLink in version 2.4. If you are using a version of that does not contain this version (i.e. WebLogic 10.3.4...
GlassFish 4 is now available offering the complete Java EE 7 (JSR-342) platform. EclipseLink made some major contributions to this release. The first is providing the JPA 2.1 (JSR-338) implementation. The second which I’ll cover in this post is EclipseLink MOXy is now the default JSON-binding provider for JAX-RS applications. RESTful Service CustomerService Normally a [...]
JAXB (JSR-222) makes it easy for you to convert instances of your domain classes to/from XML. The EclipseLink MOXy implementation offers an extension called Dynamic JAXB where instead of real classes you have instances of a map like class called DynamicEntity. You can access the data on your DynamicEntity using get and set methods that [...]
In a previous series of posts I covered how to create a JAX-RS service that leveraged JPA for the persistence layer. EclipseLink contains a component called JPA-RS that can be used to easily and automatically expose a persistence unit as RESTful service (that supports XML and JSON messages). MOXy provides the XML and JSON-binding for [...]
Java will soon have a standard set of APIs for processing JSON as part of Java EE 7. This standard is being defined as JSR 353 – Java API for JSON Processing (JSON-P) and it is currently at the Final Approval Ballot. JSON-P offers both object oriented and stream based approaches, in this post I [...]
Suppose you have a domain model that you want to expose as a RESTful service. The problem is you only want to input/output part of your data. Previously you would have created a separate model representing the subset and then have code to move data between the models. In EclipseLink 2.5.0 we have a new [...]
One of EclipseLink JAXB (MOXy)‘s strengths is the ability to map an object model to both JSON and XML with a single set of metadata. The one weakness had been that you needed to compromise on the JSON key or XML element for collection properties. I’m happy to say that this issue has been solved [...]
Is it ironic that it can be difficult to map the java.util.Map class in JAXB (JSR-222)? In this post I will cover some items that will make it much easier. Java Model Below is the Java model that we will use for this example. Customer The Customer class has a property of type Map . [...]
In previous articles I demonstrated how EclipseLink JAXB (MOXy) is directly integrated into the JAX-WS implementations in WebLogic (as of 12.1.1) and in GlassFish (as of 3.1.2). In this post I’ll demonstrate how to leverage MOXy in any application server by using the JAX-WS Provider class. Web Service The Provider mechanism in JAX-WS provides you [...]
Demo Code The following demo code will be used for all the different versions of the Java model. It simply sets one collection to null, the second to an empty list, and the third to a populated list. package package blog.xmlelementwrapper; import java.util.ArrayList; import javax.xml.bind.*; public class Demo { [...]
For some an XML schema is a strict set of rules for how the XML document must be structured. But for others it is a general guideline to indicate what the XML should look like. This means that sometimes people want to accept input that doesn’t conform to the XML schema for some reason. In [...]