I'm using EclipseLink MOXy as my JAXB (JSR-222) provider and need some help with my mapping file to marshal my classes into XML.
I'm using an external file for my mapping.
I have two types of transactions: A and B. Both contain a header object (same object) with two fields (text1 and text2).
When marshalling these into XML, I would like the xml tag for the fields of the header of transactionA to become <headerA1> and <headerA2>, and for the ones linked to transactionB to become <headerB1> and <headerB2>.
Any idea how I could accomplish that (preferably without using inheritance)?
Here is the code:
HEADER Class
public class Header {
private String text1;
private String text2;
public Header(){}
public String getText1() {
return text1;
}
public void setText1(String text1) {
this.text1 = text1;
}
public String getText2() {
return text2;
}
public void setText2(String text2) {
this.text2 = text2;
}
}
TRANSACTION A
public class TransactionA {
private Header statementHeader;
private BigDecimal units;
private BigDecimal price;
public TransactionA(){}
public BigDecimal getUnits() {
return units;
}
public void setUnits(BigDecimal units) {
this.units = units;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Header getStatementHeader() {
return statementHeader;
}
public void setStatementHeader(Header statementHeader) {
this.statementHeader = statementHeader;
}
}
TRANSACTION B
public class TransactionB {
private Header statementHeader;
private BigDecimal units;
private BigDecimal price;
public TransactionB(){}
public BigDecimal getUnits() {
return units;
}
public void setUnits(BigDecimal units) {
this.units = units;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Header getStatementHeader() {
return statementHeader;
}
public void setStatementHeader(Header statementHeader) {
this.statementHeader = statementHeader;
}
}
MAPPING FILE
<java-types>
<java-type name="Statement" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="tranA" />
<xml-element java-attribute="tranB" />
</java-attributes>
</java-type>
<java-type name="Header" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="text1" name="headerA1" />
<xml-element java-attribute="text2" name="headerA2" />
</java-attributes>
</java-type>
<java-type name="TransactionA" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="statementHeader" name="headerA" />
<xml-element java-attribute="units" />
<xml-element java-attribute="price"/>
</java-attributes>
</java-type>
<java-type name="TransactionB" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="statementHeader" name="headerB" />
<xml-element java-attribute="units" />
<xml-element java-attribute="price"/>
</java-attributes>
</java-type>
</java-types>
RESULT As you can see, the tags for header B are the same as the ones for header A.
<?xml version="1.0" encoding="UTF-8"?>
<tranA>
<headerA>
<headerA1>Description</headerA1>
<headerA2>Units</headerA2>
</headerA>
<units>10</units>
<price>99999999.98999999463558197021484375</price>
</tranA><tranB>
<headerB>
<headerA1>Bheader1</headerA1>
<headerA2>Bheader2</headerA2>
</headerB>
<units>10</units>
<price>99999999.98999999463558197021484375</price>
</tranB>
We have had an enhancement request open for this type of behaviour for a while now. If you could vote on the following but it would help move it up our priority list.
HOW YOU COULD DO THIS TODAY
XmlAdapter (HeaderBAdapter)
You could use an XmlAdapter
to provide an alternate mapping for the Header
class.
package forum13986357;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class HeaderBAdapter extends XmlAdapter<HeaderBAdapter.AdaptedHeaderB, Header>{
public static class AdaptedHeaderB {
public String headerB1;
public String headerB2;
}
@Override
public AdaptedHeaderB marshal(Header header) throws Exception {
AdaptedHeaderB adaptedHeaderB = new AdaptedHeaderB();
adaptedHeaderB.headerB1 = header.getText1();
adaptedHeaderB.headerB2 = header.getText2();
return adaptedHeaderB;
}
@Override
public Header unmarshal(AdaptedHeaderB adaptedHeaderB) throws Exception {
Header header = new Header();
header.setText1(adaptedHeaderB.headerB1);
header.setText2(adaptedHeaderB.headerB2);
return header;
}
}
oxm.xml
Below is an updated version of your mapping document. I have updated the mapping for the statementHeader
property in the TransactionB
class to specify that the XmlAdapter
should be used.
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum13986357">
<java-types>
<java-type name="Statement" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="tranA" />
<xml-element java-attribute="tranB" />
</java-attributes>
</java-type>
<java-type name="Header" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="text1" name="headerA1" />
<xml-element java-attribute="text2" name="headerA2" />
</java-attributes>
</java-type>
<java-type name="TransactionA" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="statementHeader" name="headerA" />
<xml-element java-attribute="units" />
<xml-element java-attribute="price" />
</java-attributes>
</java-type>
<java-type name="TransactionB" xml-accessor-type="NONE">
<java-attributes>
<xml-element java-attribute="statementHeader" name="headerB">
<xml-java-type-adapter value="forum13986357.HeaderBAdapter" />
</xml-element>
<xml-element java-attribute="units" />
<xml-element java-attribute="price" />
</java-attributes>
</java-type>
</java-type
s>
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 (http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
The following demo code can be used to prove that everything works.
package forum13986357;
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>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum13986357/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Statement.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource xml = new StreamSource("src/forum13986357/input.xml");
JAXBElement<Statement> jaxbElement = unmarshaller.unmarshal(xml, Statement.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jaxbElement, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<statement>
<tranA>
<headerA>
<headerA1>Description</headerA1>
<headerA2>Units</headerA2>
</headerA>
<units>10</units>
<price>99999999.98999999463558197021484375</price>
</tranA>
<tranB>
<headerB>
<headerB1>Bheader1</headerB1>
<headerB2>Bheader2</headerB2>
</headerB>
<units>10</units>
<price>99999999.98999999463558197021484375</price>
</tranB>