Jaxb with baseclass doesn't serialize derived elements - java

I have a list of modules in a tree like structure. The baseclass contains the childs and some generic property, while the derived class should provide it's individual fields. Now when I try to serialize this with JAXB I get only the members of the baseclass but not the ones from the derived classes.
Application.java
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Application
{
public static void main(String[] args)
{
try
{
Emulators emulators = new Emulators();
emulators.addChild(new VICEModule());
emulators.addChild(new VICEModule());
StringWriter sw = new StringWriter();
JAXBContext jaxbContext = JAXBContext.newInstance(emulators.getClass());
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Pretty print
jaxbMarshaller.marshal(emulators, sw);
String s = sw.toString();
System.out.println(s);
}
catch(Throwable e)
{
System.err.println("Exception:"+e);
}
}
}
ModuleBase.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import com.sun.xml.internal.txw2.annotation.XmlAttribute;
public class ModuleBase
{
private List<ModuleBase> mChilds = new ArrayList<>();
private String mModuleId;
private String mModuleName;
public ModuleBase(String oModuleId, String oModuleName)
{
setModuleId(oModuleId);
setModuleName(oModuleName);
}
public void addChild(ModuleBase oModuleNode)
{
mChilds.add(oModuleNode);
}
#XmlElement(name="ModuleList")
public List<ModuleBase> getChildModules()
{
return mChilds;
}
#XmlAttribute
public String getModuleId()
{
return mModuleId;
}
public void setModuleId(String oModuleId)
{
mModuleId = oModuleId;
}
#XmlAttribute
public String getModuleName()
{
return mModuleName;
}
public void setModuleName(String oModuleName)
{
mModuleName = oModuleName;
}
}
Emulators.java
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "Emulators")
public class Emulators
extends ModuleBase
{
public Emulators()
{
super("IdEmu129872q53", "Emulators");
}
}
VICEModule.java
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement(name = "VICEModule")
public class VICEModule
extends ModuleBase
{
private String mInstallationPath;
private int mPort;
public VICEModule()
{
super("Id123456", "Vice");
mPort = 6510;
}
//#XmlElement(name="InstallationPath")
#XmlValue
public String getInstallationPath()
{
return mInstallationPath;
}
public void setInstallationPath(String oPath)
{
mInstallationPath = oPath;
}
//#XmlElement(name="Port")
#XmlValue
public int getPort()
{
return mPort;
}
public void setPort(int nPort)
{
mPort = nPort;
}
}
Now when I serialize it, I get the following XML where the VICEModule values are missing and also the module is listed as ModuleList instead of VICEModule and the base fields are put as tags instead of attributes:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators>
<ModuleList>
<moduleId>Id123456</moduleId>
<moduleName>Vice</moduleName>
</ModuleList>
<ModuleList>
<moduleId>Id123456</moduleId>
<moduleName>Vice</moduleName>
</ModuleList>
<moduleId>IdEmu129872q53</moduleId>
<moduleName>Emulators</moduleName>
</Emulators>
So what this should look like is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators ModuleId="IdEmu129872q53" ModuleName="Emulators">
<VICEModule ModuleId="Id1" ModuleName="Name">
<InstallationPath>Path1</InstallationPath>
<Port>6510</Port>
</VICEModule>
<VICEModule ModuleId="Id2" ModuleName="Name">
<InstallationPath>Path2</InstallationPath>
<Port>6511</Port>
</VICEModule>
</Emulators>
When I use the VICEModule as the baseclass, then the XML looks more like it (still without the attributes though).

Writing this small demo app helped, because now I could more easily play around with the marshalling and finally got it working. I'm posting the changes here, to make this sample complete in case somebody needs it as a reference. Emulators.java was not changed, so it's not listed in the answer.
Short answer:
I had to add #XmlAnyElement(lax=true) to my getter for the child modules in order to convert the resulting XML node into a named node as I want it to. Additionally in the main application I had to traverse the tree and collect all classes that are used, so that I can pass it to the JAXBContext.
Application.java
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Application
{
public static List<Class<?>> createClasses(ModuleBase oModule)
{
List<Class<?>> classes = new ArrayList<>();
classes.add(oModule.getClass());
for(ModuleBase module : oModule.getChildModules())
{
List<Class<?>> cls = createClasses(module);
classes.addAll(cls);
}
return classes;
}
public static void main(String[] args)
{
try
{
Emulators emulators = new Emulators();
emulators.addChild(new VICEModule("Id1", "VICE V1", "V1 Path", 6510));
emulators.addChild(new VICEModule("Id2", "VICE V2", "V2 Path", 6511));
StringWriter sw = new StringWriter();
List<Class<?>> classes = createClasses(emulators);
Class<?>[] cls = new Class<?>[classes.size()];
for(int i = 0; i < classes.size(); i++)
cls[i] = classes.get(i);
JAXBContext jaxbContext = JAXBContext.newInstance(cls);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // Pretty print
jaxbMarshaller.marshal(emulators, sw);
String s = sw.toString();
System.out.println(s);
}
catch(Throwable e)
{
System.err.println("Exception:"+e);
}
return;
}
}
ModuleBase.java
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
public class ModuleBase
{
private List<ModuleBase> mChilds = new ArrayList<>();
private String mModuleId;
private String mModuleName;
public ModuleBase(String oModuleId, String oModuleName)
{
setModuleId(oModuleId);
setModuleName(oModuleName);
}
public void addChild(ModuleBase oModuleNode)
{
mChilds.add(oModuleNode);
}
#XmlAnyElement(lax=true)
public List<ModuleBase> getChildModules()
{
return mChilds;
}
#XmlAttribute
public String getModuleId()
{
return mModuleId;
}
public void setModuleId(String oModuleId)
{
mModuleId = oModuleId;
}
#XmlAttribute
public String getModuleName()
{
return mModuleName;
}
public void setModuleName(String oModuleName)
{
mModuleName = oModuleName;
}
}
VICEModule.java
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "VICEModule")
public class VICEModule
extends ModuleBase
{
private String mInstallationPath;
private int mPort;
public VICEModule()
{
super("Id123456", "Vice");
mPort = 6510;
}
public VICEModule(String oId, String oName, String oPath, int nPort)
{
super(oId, oName);
setInstallationPath(oPath);
setPort(nPort);
}
#XmlElement(name="InstallationPath")
public String getInstallationPath()
{
return mInstallationPath;
}
public void setInstallationPath(String oPath)
{
mInstallationPath = oPath;
}
#XmlElement(name="Port")
public int getPort()
{
return mPort;
}
public void setPort(int nPort)
{
mPort = nPort;
}
}
Now the XML renders exactly as intended with storing all the objects from derived classes.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Emulators moduleId="IdEmu129872q53" moduleName="Emulators">
<VICEModule moduleId="Id1" moduleName="VICE V1">
<InstallationPath>V1 Path</InstallationPath>
<Port>6510</Port>
</VICEModule>
<VICEModule moduleId="Id2" moduleName="VICE V2">
<InstallationPath>V2 Path</InstallationPath>
<Port>6511</Port>
</VICEModule>
</Emulators>

Related

Unable to set Pojo Object variable with an external value during JaxB MoXY Unmarshalling

I am trying the above approach that you have mentioned for NodeAdaptar, but I am getting null for my element.
Scenario:
I have to set the value of variable defined in my Java Model(POJO) Class with a value, coming externally(not part of the input XML). This value will be used in some calculation post unmarshal.
input XML: its a huge one. I am able to unmarshall it correctly.
POJO Class:
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="root")
#XmlAccessorType(XmlAccessType.FIELD)
public class MainModelClass {
#XmlPath("abc/def/Result/text()")
#XmlElement(name="Result")
private String result;
#XmlPath("/abc/def/Score/text()")
#XmlElement(name="Score")
private String score;
///This is where I want to populate the external value ///
#XmlElement
#XmlJavaTypeAdapter(PCNDateAdaptar.class)
private PCNDate pcnDateObject;
public PCNDate getPcnDateObject() {
return pcnDateObject;
}
public void setPcnDateObject(PCNDate pcnDateObject) {
this.pcnDateObject = pcnDateObject;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getScore() {
return score;
}
public void setScore(String score) {
this.score = score;
}
// Block for overridden toString() //
}
Adaptar Class:
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class PCNDateAdaptar extends XmlAdapter<Integer, PCNDate> {
private PCNDate pcnDateObject;
public void setPcnDateObject(PCNDate pcnDateob) {
this.pcnDateObject = pcnDateob;
}
#Override
public PCNDate unmarshal(Integer v) throws Exception {
if(v == null)
return this.pcnDateObject;
else
return this.pcnDateObject;
}
#Override
public Integer marshal(PCNDate v) throws Exception {
if(v == null)
return null;
else
return null;
}
}
PCNDate.class:
public class PCNDate {
private Integer processControlDate;
public Integer getProcessControlDate() {
return processControlDate;
}
public void setProcessControlDate(Integer pcn) {
this.processControlDate = pcn;
}
}
Unmarshaller Method:
public static <T> T getXMLSnippetObject(String xmlString, Class<T> modelClass, XmlAdapter<?, ?> xmlAdapterObject) throws XMLStreamException, JAXBException {
JAXBContext context = JAXBContext.newInstance(modelClass);
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setAdapter(xmlAdapterObject);
InputStream xmlInputStream = IOUtils.toInputStream(xmlString);
XMLInputFactory2 xmlInputFactory = (XMLInputFactory2)XMLInputFactory.newInstance();
xmlInputFactory.setProperty("javax.xml.stream.isCoalescing", true);
xmlInputFactory.setProperty("javax.xml.stream.isNamespaceAware", true);
xmlInputFactory.setProperty("javax.xml.stream.isReplacingEntityReferences", true);
xmlInputFactory.setProperty(XMLInputFactory2.P_AUTO_CLOSE_INPUT, true);
//xmlInputFactory.setProperty(XMLInputFactory2.P_DTD_OVERRIDE, false);
xmlInputFactory.setProperty(XMLInputFactory2.P_REPORT_PROLOG_WHITESPACE, false);
xmlInputFactory.setProperty(XMLInputFactory2.P_PRESERVE_LOCATION,false);
xmlInputFactory.setProperty(XMLInputFactory2.P_INTERN_NS_URIS, true);
XMLStreamReader2 xmlStreamReader = (XMLStreamReader2) xmlInputFactory.createXMLStreamReader(xmlInputStream);
T objectInstance = (T) JAXBIntrospector.getValue(unmarshaller.unmarshal(xmlStreamReader, modelClass));
return objectInstance;
}
Main Calling Class:
public class XMLparsing {
public static void main(String[] args) throws XMLStreamException, JAXBException, IOException {
String xmlString = // Xml String //
MainModelClass modelObj = null;
Integer pcnDate = 20171010;
// PCNDate & PCNDateAdapter are both no-arg-constructor classes for JAXB purpose
PCNDate pcnObj = new PCNDate();
pcnDateObj.setProcessControlDate(pcnDate);
PCNDateAdaptar pcndateAdaptar = new PCNDateAdaptar();
pcndateAdaptar.setPcnDateObject(pcnObj);
modelObj = XmlUtilsStAX.getXMLSnippetObject(xmlString, MainModelClass.class, pcndateAdaptar);
}
Result:
The whole Xml String is getting correctly parsed. Only MainModelClass's pcnDateObject is null, result & score have values. I want pcnDateObject to have 20171010.
I don't know, what I am missing, please help.

JAXB How To Handle Attribute with Colon (:) in tag? --> cim:ACLineSegment

I am trying to unmarshal a CIM file with JAXB. When any tag contains :(colon), the result is always null. If i remove the colons, then it works well.
An example of tags in my file:
Thank you.
<cim:ACLineSegment>
<cim:ACLineSegment.r>2.200000</cim:ACLineSegment.r>
<cim:ACLineSegment.x>68.200000</cim:ACLineSegment.x>
<cim:ACLineSegment.bch>0.0000829380</cim:ACLineSegment.bch>
</cim:ACLineSegment>
Java code here:
package JAXB_read;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="cimACLineSegment")
#XmlAccessorType(XmlAccessType.FIELD)
public class AClineSegment {
#XmlElement(name ="cimACLineSegment.r")
private double resistance;
#XmlElement(name ="cimACLineSegment.x")
private double reactance;
#XmlElement(name ="cimACLineSegment.bch")
private double susceptance;
public double getResistance() {
return resistance;
}
public void setResistance(double resistance) {
this.resistance = resistance;
}
public double getReactance() {
return reactance;
}
public void setReactance(double reactance) {
this.reactance = reactance;
}
public double getSusceptance() {
return susceptance;
}
public void setSusceptance(double susceptance) {
this.susceptance = susceptance;
}
public AClineSegment(double resistance, double reactance, double susceptance) {
super();
this.resistance = resistance;
this.reactance = reactance;
this.susceptance = susceptance;
}
public AClineSegment() {
super();
}
}
package JAXB_read;
import java.io.File;
import javax.xml.bind.*;
public class AClineSegementJAXB {
public void unmarshall()
{
try {
JAXBContext jc = JAXBContext.newInstance(AClineSegment.class);
Unmarshaller ums = jc.createUnmarshaller();
AClineSegment ACline = (AClineSegment)ums.unmarshal(new File("src\\data\\AclineSegment.txt"));
System.out.println("Informations:");
System.out.println("----------------------");
System.out.println("Resistance: "+ACline.getResistance());
System.out.println("Reactance: "+ACline.getReactance());
System.out.println("Susceptance: "+ACline.getSusceptance());
} catch (Exception e) {
System.out.println(""+e.getMessage());
}
}
}
package Main;
import JAXB_read.*;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
// AC LINE SSEGMENT
public class Main {
public static void main(String[] args)
{
AClineSegementJAXB object = new AClineSegementJAXB();
object.unmarshall();
}
}

How to return value from xpath in JAXB

I still new in JAXB, below are my codes listing.
My intention is to print this xpath //customers/customer/name by using following sample.
I have been trying to understand from #Blaise Doughan's blogs but it keep give me empty result. Anyone can pin point where is my mistakes and what should I do?
Sample XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customers>
<customer id="100">
<name>customer1</name>
<age>29</age>
</customer>
<customer id="200">
<age>39</age>
<name>customer2</name>
</customer>
</customers>
I have Main Class
package performancetest.JAXB_Unmarshalling;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class JAXBExample {
public static void main(String[] args) {
getByCustomerName();
}
public static void getByCustomerName() {
try {
JAXBContext jc = JAXBContext.newInstance(CustomerNamesList.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
CustomerNamesList obj = (CustomerNamesList)unmarshaller.unmarshal(new File("C:\\Users\\iman.solaiman\\Documents\\file.xml"));
System.out.println(obj.getCustomers());
}catch (Exception e) {
e.printStackTrace();
}
}
}
And Customer Name List
package performancetest.JAXB_Unmarshalling;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "customers")
public class CustomerNamesList {
#XmlElement(name="customer/name")
private List<CustomerNames> customerNamesList = new ArrayList<CustomerNames>();
public List<CustomerNames> getCustomers() {
return customerNamesList;
}
public void Customers(List<CustomerNames> CustomerNames) {
this.customerNamesList = CustomerNames;
}
public void getElement () {
for (int i=0; i<customerNamesList.size(); i++){
System.out.println("Element "+i+": "+customerNamesList.get(i));
}
}
}
And Customer Names
package performancetest.JAXB_Unmarshalling;
import org.eclipse.persistence.oxm.annotations.XmlPath;
public class CustomerNames {
#XmlPath("customers/customer/name/text()")
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Customer [name=" + name+ "]";
}
}
Result:
run:
[]
BUILD SUCCESSFUL (total time: 1 second)
I find that this is easier to do with the method below:
private String getXPath(String xml) throws XPathExpressionException {
String xPathResult= "";
InputSource source = new InputSource(new StringReader(xml));
XPath xpath = XPathFactory.newInstance().newXPath();
Object jaxBObject = xpath.evaluate("/customers", source, XPathConstants.NODE);
xPathResult= xpath.evaluate("name", jaxBObject);
return xPathResult;
}
This should give you the value inside the name element. I hope this is useful!

Unmarshal XML via JAXB

I have a xml as follows:
<VisitorDataList>
<VisitorData>
<Attribute1List>
<Attribute1>
<Attribute1Name>name1</Attribute1Name>
<Attribute1Id>2</Attribute1Id>
</Attribute1>
<Attribute1>
<Attribute1Name>name1</Attribute1Name>
<Attribute1Id>3</Attribute1Id>
</Attribute1>
<Attribute1>
<Attribute1Name>name1</Attribute1Name>
<Attribute1Id>1</Attribute1Id>
</Attribute1>
</Attribute1List>
<Attribute2List>
<Attribute2>
<Attribute2Name>name2</Attribute2Name>
<Attribute2Id>113</Attribute2Id>
</Attribute2>
<Attribute2>
<Attribute2Name>name2</Attribute2Name>
<Attribute2Id>115</Attribute2Id>
</Attribute2>
<Attribute2>
<Attribute2Name>name2</Attribute2Name>
<Attribute2Id>114</Attribute2Id>
</Attribute2>
</Attribute2List>
</VisitorData>
</VisitorDataList>
The Attribute1List and Attribute2List is dynamic for both name and value, so they can be Attribute3List or Attribute4List and so on. Now i want to parse this xml via jAXB, the java bean is like:
#XmlRootElement(name = "VisitorDataList")
public class VisitorDataList {
#XmlElement(name = "VisitorData")
public List<Visitor> getVisitors() {
return visitors;
}
public void setVisitors(List<Visitor> visitors) {
this.visitors = visitors;
}
}
public class Visitor {
private List<String> attributes= new ArrayList<String>();
public List<String> getAttributes() {
return attributes;
}
public void setAttributes(List<String> attributes) {
this.attributes= attributes;
}
}
My question is how to map all the attribute names into one list? Thanks in advance.
You could use JAXB to unmarshal from StAX with a StreamReaderDelegate to strip off the number from the element names. Your JAXB mappings will then be made to elements
Below is a link to an article I wrote where I use this approach to do case insensitive unmarshalling:
http://blog.bdoughan.com/2010/12/case-insensitive-unmarshalling.html
Java Model
VistorDataList
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "VisitorDataList")
public class VisitorDataList {
private List<Visitor> visitors;
#XmlElementWrapper(name="VisitorData")
#XmlElement(name = "AttributeList")
public List<Visitor> getVisitors() {
return visitors;
}
public void setVisitors(List<Visitor> visitors) {
this.visitors = visitors;
}
}
Visitor
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
public class Visitor {
private List<String> attributes = new ArrayList<String>();
#XmlElement(name="AttributeName")
public List<String> getAttributes() {
return attributes;
}
public void setAttributes(List<String> attributes) {
this.attributes = attributes;
}
}
Demo Code
Demo
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/forum18071557/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr = new StreamReaderDelegate(xsr) {
#Override
public String getLocalName() {
String localName = super.getLocalName();
if(localName.startsWith("Attribute") && localName.endsWith("List")) {
return "AttributeList";
} else if(localName.startsWith("Attribute") && localName.endsWith("Name")) {
return "AttributeName";
} else if(localName.startsWith("Attribute")) {
return "ignore";
} else {
return localName;
}
}
};
xsr = xif.createFilteredReader(xsr, new StreamFilter() {
#Override
public boolean accept(XMLStreamReader reader) {
if(reader.isStartElement() || reader.isEndElement()) {
return !reader.getLocalName().equals("ignore");
} else {
return true;
}
}
});
JAXBContext jc = JAXBContext.newInstance(VisitorDataList.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
VisitorDataList vdl = (VisitorDataList) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(vdl, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VisitorDataList>
<VisitorData>
<AttributeList>
<AttributeName>name1</AttributeName>
<AttributeName>name1</AttributeName>
<AttributeName>name1</AttributeName>
</AttributeList>
<AttributeList>
<AttributeName>name2</AttributeName>
<AttributeName>name2</AttributeName>
<AttributeName>name2</AttributeName>
</AttributeList>
</VisitorData>
</VisitorDataList>
I tried your XML file to unmarshal using JAXB but I was only able to parse it statically.You can try it once.
visitor.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<VisitorDataList>
<VisitorData>
<AttributeList>
<Attribute>
<AttributeName>name1</AttributeName>
<AttributeId>2</AttributeId>
</Attribute>
<Attribute>
<AttributeName>name1</AttributeName>
<AttributeId>3</AttributeId>
</Attribute>
<Attribute>
<AttributeName>name1</AttributeName>
<AttributeId>1</AttributeId>
</Attribute>
</AttributeList>
<AttributeList>
<Attribute>
<AttributeName>name2</AttributeName>
<AttributeId>113</AttributeId>
</Attribute>
<Attribute>
<AttributeName>name2</AttributeName>
<AttributeId>115</AttributeId>
</Attribute>
<Attribute>
<AttributeName>name2</AttributeName>
<AttributeId>114</AttributeId>
</Attribute>
</AttributeList>
</VisitorData>
visitordatalist.java
package foo.bar.me.too;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="VisitorDataList")
public class visitordatalist {
List<visitordata> vstd;
#XmlElement(name="VisitorData")
public List<visitordata> getVstd() {
return vstd;
}
public void setVstd(List<visitordata> vstd) {
this.vstd = vstd;
}
}
visitordata.java
package foo.bar.me.too;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
public class visitordata {
List<attributelist> atrl;
public String toString() {
return atrl.toString();
}
#XmlElement(name="AttributeList")
public List<attributelist> getAtrl() {
return atrl;
}
public void setAtrl(List<attributelist> atrl) {
this.atrl = atrl;
}
}
attributelist.java
package foo.bar.me.too;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
public class attributelist {
List<attribute> atr;
public String toString() {
return atr.toString();
}
#XmlElement(name="Attribute")
public List<attribute> getAtr() {
return atr;
}
public void setAtr(List<attribute> atr) {
this.atr = atr;
}
}
attribute.java
package foo.bar.me.too;
import javax.xml.bind.annotation.XmlElement;
public class attribute {
String atrName;
String atrId;
public String toString() {
return atrName+" "+atrId;
}
#XmlElement(name="AttributeName")
public String getAtrName() {
return atrName;
}
public void setAtrName(String atrName) {
this.atrName = atrName;
}
#XmlElement(name="AttributeId")
public String getAtrId() {
return atrId;
}
public void setAtrId(String atrId) {
this.atrId = atrId;
}
}
JAXBExample.java
package foo.bar.me.too;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class JAXBExample {
public static void main(String[] args) {
try {
File file = new File("visitor.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(visitordatalist.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
visitordatalist vstdl = (visitordatalist) jaxbUnmarshaller.unmarshal(file);
System.out.println(vstdl.getVstd());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
There is a special arcane knowledge for some special JAXB implementations. For example, for FasterXML (used in WildFly) you should change one property to make it work (just override an additional method in Delegator):
#Override
public Object getProperty(String name) {
Object property = super.getProperty(name);
if("org.codehaus.stax2.internNames".equals(name)) {
return Boolean.FALSE;
}
return property;
}

JAXB Serializing Interface to XML Issues (Map<String,ISomeInterface> not working)

I am attempting to serialize an interface to XML using JAXB 2.2.4, but when I have an interface within a Map<> object, it blows up and gives me the error:
com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of
IllegalAnnotationExceptions com.test.IInterface2 is an interface, and
JAXB can't handle interfaces. this problem is related to the
following location: at com.test.IInterface2 at public
java.util.Map com.test.Interface1Impl.getI2() at
com.test.Interface1Impl com.test.IInterface2 does not have a no-arg
default constructor. this problem is related to the following
location: at com.test.IInterface2 at public java.util.Map
com.test.Interface1Impl.getI2() at com.test.Interface1Impl
This code has been tested and works if I remove the Map<>, and have even gotten it to work if i use a List<>, but there is something about the Map<> that JAXB doesn't like.
Here is the code I'm running, please let me know if you know of a way to fix this!
package com.test;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({Interface2Impl.class})
public class main
{
/**
* #param args
*/
public static void main(String[] args) {
IInterface1 i1 = new Interface1Impl();
i1.setA("SET A VALUE");
i1.setB("Set B VALUE");
IInterface2 i2 = new Interface2Impl();
i2.setC("X");
i2.setD("Y");
i1.getI2().put("SOMVAL",i2);
String retval = null;
try {
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Interface1Impl.class, Interface2Impl.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(i1, writer);
retval = writer.toString();
} catch (JAXBException ex) {
//TODO: Log the error here!
retval = ex.toString();
}
System.out.println(retval);
}
}
package com.test;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.sun.xml.bind.AnyTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(AnyTypeAdapter.class)
public interface IInterface1
{
Map<String,IInterface2> getI2();
String getA();
String getB();
void setA(String a);
void setB(String b);
void setI2(Map<String,IInterface2> i2);
}
package com.test;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Interface1Impl implements IInterface1
{
Map<String,IInterface2> i2 = new HashMap<String,IInterface2>();
String a;
String b;
public Interface1Impl()
{
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public Map<String,IInterface2> getI2() {
return i2;
}
public void setI2(Map<String,IInterface2> i2) {
this.i2 = i2;
}
}
package com.test;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.sun.xml.bind.AnyTypeAdapter;
#XmlRootElement
#XmlJavaTypeAdapter(AnyTypeAdapter.class)
public interface IInterface2
{
String getC();
String getD();
void setC(String c);
void setD(String d);
}
package com.test;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Interface2Impl implements IInterface2
{
String c;
String d;
public Interface2Impl()
{
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
}
To get the following output you could do the following (see below):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<interface1Impl>
<a>SET A VALUE</a>
<b>Set B VALUE</b>
<i2>
<entry>
<key>SOMVAL</key>
<value>
<c>X</c>
<d>Y</d>
</value>
</entry>
</i2>
</interface1Impl>
I2Adapter
We will use an XmlAdapter to handle the Map<String, IInterface2>. An XmlAdapter is a JAXB mechanism that converts an object that JAXB can't map into one that it can.
package com.test;
import java.util.*;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class I2Adapter extends XmlAdapter<I2Adapter.AdaptedI2, Map<String, IInterface2>> {
#Override
public AdaptedI2 marshal(Map<String, IInterface2> v) throws Exception {
if(null == v) {
return null;
}
AdaptedI2 adaptedI2 = new AdaptedI2();
for(Map.Entry<String,IInterface2> entry : v.entrySet()) {
adaptedI2.entry.add(new Entry(entry.getKey(), entry.getValue()));
}
return adaptedI2;
}
#Override
public Map<String, IInterface2> unmarshal(AdaptedI2 v) throws Exception {
if(null == v) {
return null;
}
Map<String, IInterface2> map = new HashMap<String, IInterface2>();
for(Entry entry : v.entry) {
map.put(entry.key, entry.value);
}
return map;
}
public static class AdaptedI2 {
public List<Entry> entry = new ArrayList<Entry>();
}
public static class Entry {
public Entry() {
}
public Entry(String key, IInterface2 value) {
this.key = key;
this.value = value;
}
public String key;
#XmlElement(type=Interface2Impl.class)
public IInterface2 value;
}
}
Interface1Impl
The #XmlJavaTypeAdapter annotation is used to register the XmlAdapter. In this example we will register it on the i2 property.
package com.test;
import java.util.*;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Interface1Impl implements IInterface1 {
Map<String, IInterface2> i2 = new HashMap<String, IInterface2>();
String a;
String b;
public Interface1Impl() {
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
#XmlJavaTypeAdapter(I2Adapter.class)
public Map<String, IInterface2> getI2() {
return i2;
}
public void setI2(Map<String, IInterface2> i2) {
this.i2 = i2;
}
}
For More Information
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
http://blog.bdoughan.com/2010/07/xmladapter-jaxbs-secret-weapon.html
Below is the rest of your model with the JAXB annotations removed from the non-model classes:
main
package com.test;
import java.io.StringWriter;
import javax.xml.bind.*;
public class main {
/**
* #param args
*/
public static void main(String[] args) {
IInterface1 i1 = new Interface1Impl();
i1.setA("SET A VALUE");
i1.setB("Set B VALUE");
IInterface2 i2 = new Interface2Impl();
i2.setC("X");
i2.setD("Y");
i1.getI2().put("SOMVAL", i2);
String retval = null;
try {
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Interface1Impl.class,
Interface2Impl.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(i1, writer);
retval = writer.toString();
} catch (JAXBException ex) {
// TODO: Log the error here!
retval = ex.toString();
}
System.out.println(retval);
}
}
IInterface1
package com.test;
import java.util.Map;
public interface IInterface1 {
Map<String, IInterface2> getI2();
String getA();
String getB();
void setA(String a);
void setB(String b);
void setI2(Map<String, IInterface2> i2);
}
IInterface2
package com.test;
public interface IInterface2 {
String getC();
String getD();
void setC(String c);
void setD(String d);
}
Interface2Impl
package com.test;
public class Interface2Impl implements IInterface2 {
String c;
String d;
public Interface2Impl() {
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
}

Categories

Resources