JAXB Element Wrapper for a Single Group of Elements When Marshalling - java

I wish to have an XML structure like this:
<?xml version="1.0" encoding="UTF-8"?>
<MSG>
<CASE>
<Field1></Field1>
<Field2></Field2>
</CASE>
</MSG>
The problem is, with the #XmlElementWrapper, I need a collection of items but there will be only 1 case item. How can I have multiple root elements, for a single collection of elements? Preferably in a single class.
I want something like this, but it throws an exception.
#XmlRootElement( name="MSG")
public class XMLStructure {
#XmlElementWrapper(name="CASE")
#XmlElement(name = "Field1")
private String field1;
#XmlElementWrapper(name="CASE")
#XmlElement(name = "Field2")
private String field2;
}

In the EclipseLink MOXy implementation of JAXB (JSR-222) we have an #XmlPath extension that enables you to map this as:
#XmlRootElement( name="MSG")
#XmlAccessorType(XmlAccessType.FIELD)
public class XMLStructure {
#XmlPath("CASE/Field1/text()")
private String field1;
#XmlPath("CASE/Field2/text()")
private String field2;
}
For More Information
I have written more about the #XmlPath extension on my blog:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html

Related

Springfox Swagger ignores #XmlElement for nested objects only

I'm using springfox 3.0.0 and my XML Swagger looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<Foo>
<anotherBar>
<MyStr>string</MyStr>
</anotherBar>
<bar>
<MyStr>string</MyStr>
</bar>
<MyDate>2021-10-08T16:08:57.795Z</MyDate>
</Foo>
Here is what I would like:
<?xml version="1.0" encoding="UTF-8"?>
<Foo>
<AnotherBar>
<MyStr>string</MyStr>
</AnotherBar>
<Bar>
<MyStr>string</MyStr>
</Bar>
<MyDate>2021-10-08T16:08:57.795Z</MyDate>
</Foo>
As you can see, MyStr and MyDate are displayed correctly in the XML but not bar and anotherBar without the uppercase.
I don't know why the #XmlElement annotation is ignored by Springfox for nested objects only. But it works great with String or Instant.
Here is my Foo class:
#XmlRootElement(name = "Foo")
public class Foo {
#XmlElement(name = "MyDate")
private Instant myDate;
#XmlElement(name = "Bar")
private Bar bar;
#XmlElement(name = "AnotherBar")
private Bar anotherBar;
// Getter / Setter...
}
And here is my Bar class:
public class Bar {
#XmlElement(name = "MyStr")
private String myStr;
// Getter / Setter ...
}
Here is my XmlMapper config:
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new JaxbAnnotationModule());
I already tried to replace #XmlElement with #JacksonXmlProperty but it's the same result.
I found also this answer https://stackoverflow.com/a/64562167 but I don't want to use #JsonProperty since I need the JSON from Swagger to use attributes name.
Do you know why #XmlElement is ignored by Springfox in this case only?

Flexible JAXB XML parsing for generics and a particular type or a list of that type

I am working with a colleague's API. The API returns a Response with a list of objects or just one, singular object. The objects can be of multiple types. The return type is in XML. I am interested in parsing this XML via JAXB to get my classes, ideally in a flexible and generic way.
The following two XML responses are a sample of what I am speaking about.
Sample 1: A response with a list Jobs containing Job object.
<Response>
<Status>OK</Status>
<Jobs>
<Job>
<ID>J1</ID>
<Name>job name</Name>
</Job>
<Job>
<ID>J2</ID>
<Name>job name</Name>
</Job>
</Jobs>
</Response>
Sample 2: A response with one Job.
<Response>
<Status>OK</Status>
<Job>
<ID>J123</ID>
<Name>job name</Name>
</Job>
</Response>
At the moment, I am constructing something as follows:
#XmlRootElement(name="Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name = "Status")
protected Status status;
#XmlAnyElement(lax=true)
protected Object items;
}
Unmarshalling via
Response = (Response) unmarshaller.unmarshal(myXmlResponse);
But I'm receiving null in my items when unmarshalling Sample 1. Also, this approach gives me a bad feeling as I'm using Object as a catch-all i.e. expecting both List<Job> and Job type. What am I doing wrong? Is there a better solution? Maybe my response class can have two generics, one for list of items and another for a single item?
An approach in which the singular <Job> is converted to a list of jobs with one element would also be interesting, but I'm not sure that can be a generic without modifying the XML response.
You could do this:
#XmlRootElement(name = "Response")
public class Response {
#XmlElement(name ="Status")
private Status status;
#XmlElements({
#XmlElement(name = "Job", type = Job.class),
#XmlElement(name = "Jobs", type = Jobs.class),
})
private List<?> jobs;
}
Then Job would be:
public class Job {
#XmlElement(name = "ID")
private String id;
#XmlElement(name = "Name")
private String name;
}
And Jobs:
public class Jobs {
#XmlElement(name = "Job")
private List<Job> jobs;
}
Update to answer on comment:
This is the cleanest way I could think of for handling these described payloads. The challenge is with the <Jobs></Jobs> being there only some times.
There is a way to do it without embedded list but it is messier. I will copy it below so you can decide if you like it, or better to get another cleaner solution.
#XmlRootElement(name = "Response")
public class Response {
#XmlElement(name ="Status")
private Status status;
#XmlElement(name = "Job")
private List<Job> jobs;
#XmlElementWrapper(name = "Jobs")
#XmlElement(name = "Job")
private List<Job> jobsWrapped;
}

JAXB: how to unmarshal a List of objects of different types inside a wrapper?

I'm stucked at parsing the following xml with JAXB:
<?xml version="1.0" encoding="utf-8"?>
<dashboardreport name="exampleDashboard" version="6.5.6.1013" reportdate="2016-12-16T11:05:19.329+01:00" description="">
<data>
<incidentchartdashlet name="Incident Chart" description="" />
<chartdashlet name="WebRequestTime" showabsolutevalues="false" />
<chartdashlet name="WebServiceTime" showabsolutevalues="false" />
</data>
</dashboardreport>
I used the following java classes to unmarshal the xml:
Dashboardreport.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "dashboardreport")
public class Dashboardreport {
#XmlElementWrapper(name = "data")
#XmlElement(name = "chartdashlet")
protected List<Chartdashlet> chartdashlets;
#XmlElementWrapper(name = "data")
#XmlElement(name = "incidentchartdashlet")
protected List<Incidentchartdashlet> incidentchartdashlets;
#XmlAttribute(name = "name")
protected String name;
}
I just want to unmarshal the xml without using a wrapper class around incidentchartdashlets and chartdashlet, cause both types differ a lot.
I only can use the XmlElementWrapper annotation once, so that only chartdashlets get filled and incidentchartdashlets is null.
Is there any solution with JAXB without using a seperate wrapper class?
I assume your dashlet classes are defined like
class Chartdashlet extends Dashlet and class Incidentchartdashlet extends Dashlet.
Then the preferred JAXB way to handle your mixed list of dashlets would be
by using the #XmlElements
annotation:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "dashboardreport")
public class Dashboardreport {
#XmlElementWrapper(name = "data")
#XmlElements({
#XmlElement(name = "chartdashlet", type = Chartdashlet.class),
#XmlElement(name = "incidentchartdashlet", type = Incidentchartdashlet.class)
})
protected List<Dashlet> dashlets;
#XmlAttribute(name = "name")
protected String name;
}

How to map complex XML element to Java class property using JAXB

I need to map my XML snippet onto Java class using JAXB, but have a tricky case. I have the following XML:
<person>
<name part="first">Richard</name>
<name part="last">Brooks</name>
</person>
and need to map it onto the following class
public class Person {
private String firstName;
private String lastName;
}
Could you please help me to figure out JAXB annotations to make it possible?
You can do this with MOXy, see #XmlPath.
#XmlPath("name[#part='first']/text()")
private String firstName;
#XmlPath("name[#part='last']/text()")
private String lastName;
Related questions:
Using #XmlPath with jaxb/MOXy to map complex type
Here is one approach you could take, but would require you to create a separate class for Name:
#XmlRootElement
public class Person {
#XmlElement(name="name")
private List<Name> names;
...
}
public class Name {
#XmlAttribute
private String part; //would be set to "first" or "last"
#XmlValue
private String nameValue;
...
}

Unmarshalling the middle of xml document using jaxb

I am trying to unmarshall the middle elements of a big xml document. Currently using JAXB and Woodstox.
Example of xml middle elements that I need to unmarshall:
<Values>
<Person ID="ABC">
<FirstName>Shawn</FirstName>
<LastName>Mark</LastName>
<Age>3</Age>
</Person>
<Person ID="DEF">
<FirstName>John</FirstName>
<LastName>Durell</LastName>
<Age>4</Age>
</Person>
</Values>
The jaxb classes that I use are:
#XmlRootElement(name = "Values")
#XmlAccessorType(XmlAccessType.FIELD)
public class Attributes
{
#XmlElement(name = "Person")
private ArrayList<Person> persons;
public ArrayList<Person> getPersons()
{
return persons;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Person
{
#XmlAttribute
private String ID;
#XmlElement(name = "FirstName")
private String firstName;
#XmlElement(name = "LastName")
private String lastName;
#XmlElement(name = "Age")
private String age;
}
I am able to unmarshall all values except the ID. Its being shown as null.
Here is the code:
final XMLInputFactory xif = XMLInputFactory.newInstance();
final StreamSource xml = new StreamSource(pathToxmlFile);
XMLStreamReader xsr;
xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while (!xsr.getLocalName().equals("Values"))
{
xsr.nextTag();
}
final JAXBContext jc = JAXBContext.newInstance(Attributes.class);
final Unmarshaller unmarshaller = jc.createUnmarshaller();
final JAXBElement<Attributes> jb = unmarshaller.unmarshal(xsr, Attributes.class);
The above code is working only when the <Values> is nested 5-6 levels from the root. If there exists 15 tags before <Values>, this code isn't working.
Also its comparatively very slow when compared to just only using JAXB and unmarshalling all elements, but that would require me to create objects for data which will never be used.
So, my questions are -- Is there anyway to increase the performance?
Why wouldn't it work when its nested deep in the xml?
How to get the ID value from Person attribute?
The following should help:
Why wouldn't it work when its nested deep in the xml?
If by not working you mean throwing an exception like:
Exception in thread "main" javax.xml.stream.XMLStreamException: ParseError at [row,col]:[4,13]
Message: found: CHARACTERS, expected START_ELEMENT or END_ELEMENT
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.nextTag(XMLStreamReaderImpl.java:1247)
at blog.stax.middle.UnmarshalDemo.main(UnmarshalDemo.java:15)
The you could change the code that advances the XmlStreamReader to:
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName().equals("Values")) {
break;
}
xsr.next();
}
Is there anyway to increase the performance?
StAX is a very fast way to parse an XML document. It is probably being used by your JAXB implementation anyways. String comparison can be slow.
Since you are using Woodstox and it interns element names (see: section 6.1 String interning: http://woodstox.codehaus.org/FAQ). You could do identity checks on the strings instead of the equals methods.
if(Boolean.TRUE.equals(xsr.getProperty("org.codehaus.stax2.internNames"))) {
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName() == "return") {
break;
}
xsr.next();
}
} else {
while(xsr.hasNext()) {
if(xsr.isStartElement() && xsr.getLocalName().equals("return")) {
break;
}
xsr.next();
}
}
How to get the ID value from Person attribute?
By default the XML your JAXB (JSR-222) implementation will be map your ID field to an attribute called id and not ID. You can override this default as follows:
#XmlAttribute(name="ID")
private String ID;

Categories

Resources