JaxB unmarshal custom xml - java

I'm currently attempting to unmarshal some existing XML into a few classes I have created by hand. Problem is, I always get an error that tells me, JaxB expects a weather element but finds a weather element. (?)
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.aws.com/aws", local:"weather"). Expected elements are <{}api>,<{}location>,<{}weather>
I tried with and without "aws:" in the elements' name.
Here's my weather class:
#XmlRootElement(name = "aws:weather")
public class WeatherBugWeather
{
private WeatherBugApi api;
private List<WeatherBugLocation> locations;
private String uri;
#XmlElement(name="aws:api")
public WeatherBugApi getApi()
{
return this.api;
}
#XmlElementWrapper(name = "aws:locations")
#XmlElement(name = "aws:location")
public List<WeatherBugLocation> getLocations()
{
return this.locations;
}
public void setApi(WeatherBugApi api)
{
this.api = api;
}
public void setLocations(List<WeatherBugLocation> locations)
{
this.locations = locations;
}
#XmlAttribute(name="xmlns:aws")
public String getUri()
{
return this.uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
}
And that's the XML I try to parse:
<?xml version="1.0" encoding="utf-8"?>
<aws:weather xmlns:aws="http://www.aws.com/aws">
<aws:api version="2.0" />
<aws:locations>
<aws:location cityname="Jena" statename="" countryname="Germany" zipcode="" citycode="59047" citytype="1" />
</aws:locations>
</aws:weather>
I'm not quite sure what I'm doing wrong. Any hints? I suspect the problem to be the xmlns definition, but I have no idea what to do about it. (You can see that by looking at the uri-property. That was one unsuccessful idea. ^^) And yes, I did try to set the namespace but that rather set's the namespace's uri instead of it's ... name.

I would recommend adding a package-info class in with your domain model with the #XmlSchema annotation to specify the namespace qualification:
package-info
#XmlSchema(
namespace = "http://www.aws.com/aws",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.example.foo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Note
Your XmlRootElement and #XmlElement annotation should not contain the namespace prefix. You should have #XmlRootElement(name = "weather") instead of #XmlRootElement(name = "aws:weather")
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

you need namespaces in your code. namespace prefixes are meaningless, you need the actual namespace (i.e. "http://www.aws.com/aws").
#XmlRootElement(name = "weather", namespace="http://www.aws.com/aws")

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).

How can I unmarshal an XML response to 2 java objects using JAXB when there are 2 namespaces?

Thank you for taking the time to read.
My goal is to deserialize the response from an API request into 2 usable java objects.
I am sending an POST request to an endpoint to create a job in our schedule. The job is created successfully and the following XML is returned in the body:
<entry xmlns="http://purl.org/atom/ns#">
<id>0</id>
<title>Job has been created.</title>
<source>com.tidalsoft.framework.rpc.Result</source>
<tes:result xmlns:tes="http://www.auto-schedule.com/client">
<tes:message>Job has been created.</tes:message>
<tes:objectid>42320</tes:objectid>
<tes:id>0</tes:id>
<tes:operation>CREATE</tes:operation>
<tes:ok>true</tes:ok>
<tes:objectname>Job</tes:objectname>
</tes:result>
</entry>
When I attempt to only capture the elements id, title, and source the data is captured successfully. The problem is when I introduce the child object, which attempts to capture the data in the tes:result element.
Here's what the parent POJO looks like:
#XmlRootElement(name = "entry")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
private String id;
private String title;
private String source;
ResponseDetails result;
public Response() {}
}
and here is the child object:
#XmlAccessorType(XmlAccessType.FIELD)
public class ResponseDetails {
#XmlElement(name = "tes:message")
String message;
#XmlElement(name = "tes:objectid")
String objectid;
#XmlElement(name = "tes:operation")
String operation;
#XmlElement(name = "tes:ok")
String ok;
#XmlElement(name = "tes:objectname")
String objectname;
}
Finally, here is the package-info.java file I am using:
#XmlSchema(
namespace = "http://purl.org/atom/ns#",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.netspend.raven.tidal.request.response;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Any ideas are greatly appreciated. Thanks.
The problem is: Your Java code doesn't correctly specify the namespaces
corresponding to the inner XML element
<tes:result xmlns:tes="http://www.auto-schedule.com/client">
<tes:message>Job has been created.</tes:message>
<tes:objectid>42320</tes:objectid>
<tes:id>0</tes:id>
<tes:operation>CREATE</tes:operation>
<tes:ok>true</tes:ok>
<tes:objectname>Job</tes:objectname>
</tes:result>
The <tes:result> XML element has the namespace "http://www.auto-schedule.com/client".
The namespace prefix tes: itself is irrelevant for Java.
(XML namespace prefixes were invented only for better readability of the XML code.)
Therefore in your Response class, instead of just writing
ResponseDetails result;
you need to write
#XmlElement(namespace = "http://www.auto-schedule.com/client")
ResponseDetails result;
See also the javadoc of XmlElement.namespace.
Also all the XML sub-elements within the <tes:result> are specified
with this namespace "http://www.auto-schedule.com/client".
Therefore, also inside your ResponseDetails you have to correct
the namespace stuff. For example, instead of
#XmlElement(name = "tes:message")
String message;
you need to write
#XmlElement(name = "message", namespace = "http://www.auto-schedule.com/client")
String message;
You may also omit the name = "message" and simply write
#XmlElement(namespace = "http://www.auto-schedule.com/client")
String message;
because JAXB picks up this name from your Java property name message.
And similar for all the other properties in this class.

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.

Hypermedia with Jersey using Atom

Every book on REST uses <atom:link href="..." rel="..."> to define Hypermedia in RESTful apps; but Jersey (with the use of JAXB) do not seems to have this support.
I've tried #XmlSchema in package-info.java as explained here; I've also tried extendingNamespacePrefixMapper as explained there. But none works and output this at best:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns:ns2="http://www.w3.org/2005/Atom">
<first-name>Roland</first-name>
<ns2:link href="/customer/1" rel="self" />
</customer>
Using namespace and, as a result, Atom, seems impossible in Jersey. I've miss something?
ps. I'me using a XSD to generate #XmlElement classes, and, for the moment, I create my own Link class. Is there a schema or a JAR to do that (jersey-atom maven dependency uses rome but without any help)
(Assuming that you are not concerned with the namespace prefix and just want to create the links)
Here is my approach to creating the links. In my resource class (the jersey rest service), I return a java object (below "Person"), whose class is decorated with jaxb annotations. One of the properties returns atom link objects.
#XmlRootElement(namespace = Namespace.MyNamespace)
public class Person implements Serializable {
private AtomLinks links = null;
#XmlElement(name = "link", namespace = Namespace.Atom)
public AtomLinks getLink() {
if (this.links == null) {
this.links = new AtomLinks();
}
return this.links;
}
..
}
#XmlAccessorType(value = XmlAccessType.NONE)
public class AtomLinks extends ArrayList<AtomLink> {
..
}
#XmlAccessorType(value = XmlAccessType.NONE)
public class AtomLink implements Serializable {
#XmlAttribute(name = "href")
public URI getHref() {
return href;
}
#XmlAttribute(name = "rel")
public String getRel() {
return rel;
}
#XmlAttribute(name = "type")
public String getType() {
return type;
}
#XmlAttribute(name = "hreflang")
public String getHreflang() {
return hreflang;
}
#XmlAttribute(name = "title")
public String getTitle() {
return title;
}
..
}
public class Namespace {
public final static String Atom = "http://www.w3.org/2005/Atom";
..
}
Prior to returning my object ("Person") I fill in the links, creating a self link and links to other related links. I utilize the uriInfo object that jersey injects to get the base url. If this is helpful but you would like more of the example, let me know and I will fill in the gaps.
If I am right there is an approach in Jersey to inject the links to the objects.
See: Jersey 2.9 User Guide Chapter 6.

How can I marshall a single java bean into a complex XML document with existing annotations?

I have a single java bean, which is already annotated for JPA, that I also wish to store as XML, specifically FIXML. The goal is to manage the mapping from bean to XML with annotations.
I see related topics online about specifying a schema and letting JAXB generate classes, but I don't want to do that.
I've been looking at using JAXB annotations, but it seems that I'll need to make new classes for each child element. I'm trying to stay away from that, and let the annotations show how to construct the child elements. JAXB does not seem to want to do this.
Is this possible, and how? Do I need to make my own annotations and forget about JAXB?
Concrete example
Bean:
#Entity
#XmlRootElement(name="FIXML")
#XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
private String account;
private String senderCompID;
#Column(name="ACCOUNT", nullable=true, length=64)
#XmlAttribute(name="Acct")
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name="SENDER_COMP_ID", nullable=true, length=200)
#XmlAttribute(name="SID")
public String getSenderCompID() {
return this.senderCompID;
}
public void setSenderCompID(String senderCompID) {
this.senderCompID = senderCompID;
}
}
Parsing:
JAXBContext context = JAXBContext.newInstance(ExecutionReport.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //pretty print XML
marshaller.marshal(executionReport, System.out);
Desired resulting XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML>
<ExecRpt Acct="account_data">
<Hdr SID="sender"/>
</ExecRpt>
</FIXML>
Current resulting XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML Acct="account_data" SID="sender"/>
Clearly I'm not providing enough information to map the child elements yet, but I'm also not sure how to provide it. I want to add some #XmlElement annotations, but I don't have child objects, all the data is in this class.
The upside is that my XML isn't much more complicated than this example; there are only a handful of elements, and they only appear once per message. The thing that's giving me trouble is getting multiple elements out of a single bean.
You can use the #XmlPath extension in EclipseLink JAXB (MOXy) for this, I'm the tech lead.
Model Class
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#Entity
#XmlRootElement(name="FIXML")
#XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
private String account;
private String senderCompID;
#Column(name="ACCOUNT", nullable=true, length=64)
#XmlPath("ExecRpt/#Acct")
public String getAccount() {
return this.account;
}
public void setAccount(String account) {
this.account = account;
}
#Column(name="SENDER_COMP_ID", nullable=true, length=200)
#XmlPath("ExecRpt/Hdr/#SID")
public String getSenderCompID() {
return this.senderCompID;
}
public void setSenderCompID(String senderCompID) {
this.senderCompID = senderCompID;
}
}
Demo Code
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExecutionReport.class);
ExecutionReport er = new ExecutionReport();
er.setAccount("account_data");
er.setSenderCompID("sender");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(er, System.out);
}
}
Resulting XML
<?xml version="1.0" encoding="UTF-8"?>
<FIXML>
<ExecRpt Acct="account_data">
<Hdr SID="sender"/>
</ExecRpt>
</FIXML>
Specifying the EclipseLink JAXB (MOXy) Implementation
To specify MOXy as the JAXB implementation you need to add a file called jaxb.properties in with your ExecutionReport class with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
I don't know Jaxb annotations but if you ask for an attribute in ExecRpt it seems normal to have an attribute in ExecRpt no?
I think you expect a bit too much of these annotations. Don't you also want an annotation that would take a string, split it with a separator and generate a list of child elements or something?
And it seems to me a bad design to put these annotations directly on JPA entities. One day you could have to do some database changes for performances issues for exemple and you could not be able to generate the xml you want anymore. Why not transforming your jpa entity to a given structure jaxb friendly so that you keep the db and marshalling appart? Thus on change you would just have to modify the transformer.

Categories

Resources