How to convert XML to Object using JAXB - java

My client is using DropWizard/Jersey.
I am getting a response back in the form of xml. It looks like this:
I've created a file called package-info.java with following contents:
#XmlSchema(
namespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.aerstone.services.core.handlerpojos.amazon;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Finally, I have a POJO which looks like this. For now I'm just trying to map the title and ASIN.
#XmlRootElement(name="ItemSearchResponse")
public class AmazonItem
{
private String name;
private String asin;
public AmazonItem(){}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="Title")
public String getName()
{
return name;
}
public void setName(String name){this.name = name;}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="ASIN")
public String getAsin(){ return asin;}
public void setAsin(String asin){ this.asin = asin; }
}
I'm using it like this:
JAXBContext context = JAXBContext.newInstance(AmazonItem.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
newItem = (AmazonItem) unMarshaller.unmarshal(response);
But I'm getting this error:
! javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"ItemLookupResponse"). Expected elements are <{http://webservices.amazon.com/AWSECommerceService/2011-08-01}ItemSearchResponse>

You are attempting to unmarshal a document that starts with:
<ItemLookupRespons>
Instead of the XML document you have in your question that starts with:
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
If you created your JAXB model from an XML schema where the document you are trying to unmarshal is valid then you should create your JAXBContext on the package name of the generated model, or on the ObjectFactory class that was generated to pull in all the classes for the model.

Related

Unmarshall inner CDATA in xml String

I need to unmarshall the following xml String named retornoExtrato in my code
<?xml version="1.0" encoding="UTF-8" ?>
<extrato xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<erro>
<codigo/>
<descricao/>
</erro>
<consultaextrato>
<header><![CDATA[SOME MULTIPLE
LINES HEADER]]></header>
<body><![CDATA[SOME MULTIPLE
LINES BODY]]></body>
<trailer><![CDATA[SOME MULTIPLE
LINES TRAILER]]></trailer>
</consultaextrato>
</extrato>
into an Extrato object, here are my classes (constructors, getters and setters ommited when default)
#XmlRootElement(name = "extrato")
public class Extrato {
private Erro erro;
private ConsultaExtrato consultaExtrato;
}
#XmlRootElement(name = "erro")
public class Erro {
private String codigo;
private String descricao;
}
#XmlRootElement(name = "consultaextrato")
public class ConsultaExtrato {
private String header;
private String body;
private String trailer;
#XmlCDATA
public String getHeader() {
return header;
}
#XmlCDATA
public String getBody() {
return body;
}
#XmlCDATA
public String getTrailer() {
return trailer;
}
}
The situation is when unmarshalling:
Erro always get umarshelled
ConsultaExtrato is getting null
Unmarshaller jaxbUnmarshaller = JAXBContext.newInstance(Extrato.class).createUnmarshaller();
Extrato extrato = (Extrato) jaxbUnmarshaller.unmarshal(new StringReader(retornoExtrato));
On the other hand, if I create a xml with only the consultaextrato tag, it gets unmarshelled ok. But it doesn't seems to work as an inner tag.
I've tried some extra jaxb annotation in all classes, none worked. What am I missing here?
You need to tell JAXB that the XML element <consultaextrato>
within the <extrato> element corresponds to
the Java property consultaExtrato in your Extrato class.
You do this by annotating this property (or rather its getter or setter method)
with #XmlElement and giving the XML name there:
#XmlElement(name = "consultaextrato")
If you don't do this, then JAXB would derive the XML element name
from the Java property name (i.e. consultaExtrato) and thus
get no match because of the different spelling.
And by the way: The #XmlRootElement(name = "consultaextrato")
has an effect only if the <consultaextrato> is the root element
of your XML content, but not if <consultaextrato> is a nested
element within another element (in your case within <extrato>
element).

Unmarshalling a simple XML

I am having hard time unmarshalling the following XML using JAXB. This is the xml which is having only one field with an attribute. I have referred many tutorials where they only do an example of reading a string in fields which s not helpful in my case.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CircuitImpactConfigs id="1">
<Objects>
<Object type="1" impactAnalysisDataBuilderClassName="tttttttt"/>
<Object type="2" impactAnalysisDataBuilderClassName="yyyyyyyyy"/>
<Object type="3" impactAnalysisDataBuilderClassName="eeeeeee" />
<Object type="4" impactAnalysisDataBuilderClassName="iiiiiiiii"/>
<Object type="5" impactAnalysisDataBuilderClassName="rrrrrrrrrr"/>
<Object type="6" impactAnalysisDataBuilderClassName="zzzzzz"/>
<Object type="7" impactAnalysisDataBuilderClassName="qqqqqqqqqqq"/>
</Objects>
<ForceSwitchMode name="FORCE_SWITCHED" />
</CircuitImpactConfigs>
Based on what i learnt from tutorial
My Classes are CircuitImpactConfigs.java
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "CircuitImpactConfigs")
#XmlAccessorType(XmlAccessType.FIELD)
public class CircuitImpactConfigs {
private ForceSwitchMode ForceSwitchMode;
List<Obj> Object;
#XmlElement
public List<Obj> getObject() {
return Object;
}
public void setObject(List<Obj> object) {
Object = object;
}
#XmlElement
public ForceSwitchMode getForceSwitchMode() {
return ForceSwitchMode;
}
public void setForceSwitchMode(ForceSwitchMode forceSwitchMode) {
ForceSwitchMode = forceSwitchMode;
}
}
and
import javax.xml.bind.annotation.XmlAttribute;
public class ForceSwitchMode {
private String name;
#XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and
import javax.xml.bind.annotation.XmlAttribute;
public class Obj {
String type;
String impactAnalysisDataBuilderClassName;
#XmlAttribute
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#XmlAttribute
public String getImpactAnalysisDataBuilderClassName() {
return impactAnalysisDataBuilderClassName;
}
public void setImpactAnalysisDataBuilderClassName(String impactAnalysisDataBuilderClassName) {
this.impactAnalysisDataBuilderClassName = impactAnalysisDataBuilderClassName;
}
}
I am getting null list when doing the unmarshalling. This is the class where i create the JAXBcontext object and create unmarshalling object.
import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class CircuitImpactConfUnmarshaller {
public static void main(String[] args) throws JAXBException {
File file = new File("CircuitImpact.xml");
System.out.println(file.exists());
JAXBContext jaxbContext = JAXBContext.newInstance(CircuitImpactConfigs.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
CircuitImpactConfigs que = (CircuitImpactConfigs) jaxbUnmarshaller.unmarshal(file);
System.out.println(que.getForceSwitchMode().getName());
List<Obj> list = que.getObject();
System.out.println(list);
}
}
the last print statement is giving null. I understand i am doing something wrong in the class Obj
JAXB uses implicit naming conventions and explicit annotations to define a mapping between a XML and a Java structure.
Either element and attribute names in the XML match field names in Java (match by naming convention) or you need to use annotations to establish the mapping.
The Java list CircuitImpactConfigs.Object is not getting filled because the mapping failed, since the corresponding element in the XML is named Objects.
You can now either rename CircuitImpactConfigs.Object to CircuitImpactConfigs.Objects or use the name parameter of a JAXB annotation to define the corresponding name:
#XmlElement(name="Objects")
public List<Obj> getObject() {
EDIT: As you indicate in your comments there are still other mapping issues with your code. I would suggest that you adapt another approach:
Create a CircuitImpactConfigs object with all subobjects filled.
Marhsall that object to a XML file.
Check that the XML is in the expected format. If not, tweak the mapping.
Following this approach you can be sure that a XML file in the desired format can be unmarshalled to your Java structure. The code to marshall is
CircuitImpactConfigs que = ...
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(que, System.out);

Unmarshalling fails with no errors when setting namespace to #XmlRootElement

I have created a JAXB object and I am trying to unmarshal an xml string into it.
The problem that I am facing is that when I put the namespace property in the #XmlRootElement and in the xml document that I am sending, the JAXB object is getting created but it is empty. If I remove the namespace it works. So here is what I mean
My JAXB Object:
#XmlRootElement(name = "incident", namespace = "http://www.ba.com/schema/BAserviceDeskAPI/incident")
#XmlAccessorType(XmlAccessType.FIELD)
public class Incident {
#XmlElement
private String eventTitle;
public Incident() {
}
public String getEventTitle() {
return eventTitle;
}
public void setEventTitle(String eventTitle) {
this.eventTitle = eventTitle;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Incident [");
builder.append("eventTitle=");
builder.append(eventTitle);
builder.append("]");
return builder.toString();
}
}
My Main:
public static void main(String[] args) throws JAXBException {
String s = "<incident xmlns=\"http://www.ba.com/schema/BAserviceDeskAPI/incident\">"
+ "<eventTitle>Test Title from BAwrapper</eventTitle>"
+ "</incident>";
JAXBContext jaxbContext = JAXBContext.newInstance(Incident.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Incident incident = (Incident) jaxbUnmarshaller.unmarshal(new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8)));
System.out.println(incident.toString());
}
}
Output:
Incident [eventTitle=null]
If I remove the , namespace = "http://www.ba.com/schema/BAserviceDeskAPI/incident" from the #XmlRootElement and the xmlns=\"http://www.ba.com/schema/BAserviceDeskAPI/incident\" from the xml sent I get the output below
Incident [eventTitle=Test Title from BAwrapper]
Any ideas why this happens?
Thanks
The namespace specified on #XmlRootElement only applies to that element. If you want it to apply to all the elements you have mapped to, you can do it at the package level using the #XmlSchema annotation.
package-info.java
#XmlSchema(
namespace = "http://www.ba.com/schema/BAserviceDeskAPI/incident",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
I have written more about JAXB and namespace qualification on my blog:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Additional Info
Unmarshalling fails with no errors when setting namespace to
#XmlRootElement
For JAXB we (the JSR-222 expert group) decided that an unmarshal shouldn't fail by default if there is unmapped content. Why? Because alot of XML documents contain extra content and things would be failing all the time. If you do want to see these errors then you can specify a ValidationEventHandler on the Unmarshaller.

JaxB Unmarshal Exception: javax.xml.bind.UnmarshalException: unexpected element. Where have i gone wrong?

I have two bean directories, one for request beans and the other for response beans, each containing their own ObjectMapper class.
I was able to succecssfully marshall my XML Beans (for the request) to a XML String before sending it off using the following below.
JAXBElement<RequestblockType> requestblock = objectFactory.createRequestblock(requestblockType);
m.marshal(requestblock, writer);
// output string to console
byte[] bytes = writer.toString().getBytes(Charset.forName("UTF-8"));
I got my response back from server as an xml string and now trying to unmarshall back into my objects.
JAXBContext jaxbContext = JAXBContext.newInstance(ResponseblockType.class);
StringReader reader = new StringReader(xmlString);
jaxbContext.createUnmarshaller().unmarshal(reader); //this line throws Exception
um.unmarshal(reader);
On the line specified i get following exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"responseblock"). Expected elements are <{}responseblockType>
Below is my XML and the top level bean class which is off the most importance
<?xml version="1.0" encoding="UTF-8"?>
<responseblock version="3.67">
<requestreference>X5727835</requestreference>
<response type="AUTH">
<merchant>
<merchantname>Merchant1</merchantname>
<orderreference>8900001233</orderreference>
<tid>12341234</tid>
<merchantnumber>00000000</merchantnumber>
<merchantcountryiso2a>GB</merchantcountryiso2a>
</merchant>
<transactionreference>3-9-1598316</transactionreference>
<timestamp>2014-08-18 11:56:40</timestamp>
<acquirerresponsecode>00</acquirerresponsecode>
<operation>
<accounttypedescription>ECOM</accounttypedescription>
</operation>
<settlement>
<settleduedate>2014-08-18</settleduedate>
<settlestatus>0</settlestatus>
</settlement>
<billing>
<amount currencycode="USD">3698</amount>
<payment type="VISA">
<issuer>Test Issuer1</issuer>
<pan>1234123412341234</pan>
<issuercountry>US</issuercountry>
</payment>
<dcc enabled="0" />
</billing>
<authcode>TEST</authcode>
<live>0</live>
<error>
<message>Ok</message>
<code>0</code>
</error>
<security>
<postcode>0</postcode>
<securitycode>2</securitycode>
<address>0</address>
</security>
</response>
</responseblock>
package com.test.adapter.payment.test1.client.model.beans.responses;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "responseblockType")
#XmlRootElement
public class ResponseblockType {
#XmlElement(required = true)
protected String requestreference;
#XmlElement(required = true)
protected ResponseType response;
#XmlAttribute(name = "version")
protected String version;
public String getRequestreference() { return requestreference; }
public void setRequestreference(String value) { this.requestreference = value; }
public ResponseType getResponse() { return response; }
public void setResponse(ResponseType value) { this.response = value; }
public String getVersion() { return version; }
public void setVersion(String value) { this.version = value; }
}
Can anybody let me know where im going wrong?
I can supply more information if possible. I saw from other post that adding an #XMLRootElement to my top level bean response class could help but it hasnt. Beside i didnt have to do this for my request when marshalling.
The root element name of the serialized XML responseblock differs from the one defined within
the bean class.
<responseblock version="3.67">
but
#XmlRootElement
public class ResponseblockType
It should be the following to match your annotation:
<responseblockType version="3.67">
...
...
</responseblockType>
Or you can override the default root element name in the #XmlRootElement annotation:
#XmlRootElement(name="responseblock")
public class ResponseblockType
Another issue is that within the xml the root element responseblock isn't closed correctly at the end. The same problem seems to apply for the element response which s opened at line 4 of your xml
line 4: <response type="AUTH">
but not closed.

#XmlElement with multiple names

I have a situation here, trying to act as a gateway between two APIs. What I need to do, is:
make a request to an APIa;
parse (marshal) the XML response into an java object;
make little changes to it;
and then give a response in XML (unmarshal) to the other end (APIb).
The thing is that I use the same object to parse the API response and to send the response to the other end.
public class ResponseAPI{
#XmlElement(name="ResponseCode") //I receive <ResponseCode> but I need to send <ResultCode>
private String responseCode;
//getter and setter
}
as the comment says: I receive but I need to send
Is there a way to get this done without having to create another extra class which carries ResultCode?
thanks in advance!
You can try next solution using #XmlElements annotaion
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI
{
#XmlElements(
{
#XmlElement(name = "ResponseCode"),
#XmlElement(name = "ResultCode")
})
private String responseCode;
// ...
}
In this case both ResponseCode and ResultCode will be used during unmarshalling (xml -> object) and only ResultCode during marshalling (object -> xml).
So you can unmarshall XML like
<responseAPI>
<ResponseCode>404</ResponseCode>
</responseAPI>
After marshalling object will looks like
<responseAPI>
<ResultCode>404</ResultCode>
</responseAPI>
Note:
The answer given by Ilya works but isn't guaranteed to work across all implementations of JAXB or even across versions of a single JAXB implementation. The #XmlElements annotation is useful when the decision of which element to marshal depends on the type of the value (see: http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html). In your use case both the ResponseCode and ResultCode elements correspond to type String, unmarshalling will always work fine, but the choice of which element to output is arbitrary. Some JAXB Impls may have last specified wins, but others could easily have first wins.
You could do the following by leveraging #XmlElementRef.
Java Model
ResponseAPI
We will change the responseCode property from type String to JAXBElement<String>. The JAXBElement allows us to store the element name as well as the value.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseAPI{
#XmlElementRefs({
#XmlElementRef(name = "ResponseCode"),
#XmlElementRef(name = "ResultCode")
})
private JAXBElement<String> responseCode;
public JAXBElement<String> getResponseCode() {
return responseCode;
}
public void setResponseCode(JAXBElement<String> responseCode) {
this.responseCode = responseCode;
}
}
ObjectFactory
The #XmlElementRef annotations we used on the ResponseAPI class correspond to #XmlElementDecl annotations on a class annotated with #XmlRegistry. Traditionally this class is called ObjectFactory but you can call it anything you want.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="ResponseCode")
public JAXBElement<String> createResponseCode(String string) {
return new JAXBElement<String>(new QName("ResponseCode"), String.class, string);
}
#XmlElementDecl(name="ResultCode")
public JAXBElement<String> createResultCode(String string) {
return new JAXBElement<String>(new QName("ResultCode"), String.class, string);
}
}
Demo Code
input.xml
<responseAPI>
<ResponseCode>ABC</ResponseCode>
</responseAPI>
Demo
When creating the JAXBContext we need to ensure that we include the class that contains the #XmlElementDecl annotations.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ResponseAPI.class, ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("Scratch/src2/forum24554789/input.xml");
ResponseAPI responseAPI = (ResponseAPI) unmarshaller.unmarshal(xml);
ObjectFactory objectFactory = new ObjectFactory();
String responseCode = responseAPI.getResponseCode().getValue();
JAXBElement<String> resultCodeJAXBElement = objectFactory.createResultCode(responseCode);
responseAPI.setResponseCode(resultCodeJAXBElement);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(responseAPI, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<responseAPI>
<ResultCode>ABC</ResultCode>
</responseAPI>

Categories

Resources