Unmarshal XML with dynamic root element using JAXB - java

I'm trying to integrate with a third-party system and depending on the type of object, the root element of the returned XML document changes. For example:
GET /objecttype1-1/ returns:
<?xml version="1.0" encoding="UTF-8"?>
<objecttype1 xmlns="path">
<id>1</id>
<description>obj1</description>
</objecttype1>
and:
GET /objecttype2-3 returns:
<?xml version="1.0" encoding="UTF-8"?>
<objecttype2 xmlns="path">
<id>3</id>
<address>home</address>
</objecttype2>
Since the sub-elements are not guaranteed to be the same (other than id), I figured a List with #XmlMixed #XmlAnyElement will take care of them. But how do I map the root elements? #XmlRootElement(name="???")
Due to technology limitations, I'm not able to use EclipseLink/MOXy. Thanks.

Its been 5 years since this question was asked but I found working solution of the issue, sharing for the community.
First we define JAXB beans as follows:
#XmlRootElement(name = "objecttype1")
#XmlAccessorType(XmlAccessType.NONE)
public class Objecttype1 {
#XmlElement(name = "id")
private String id;
#XmlElement(name = "description")
private String description;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
#XmlRootElement(name = "objecttype2")
#XmlAccessorType(XmlAccessType.NONE)
public class Objecttype2 {
#XmlElement(name = "id")
private String id;
#XmlElement(name = "address")
private String address;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setDescription(String address) {
this.address = address;
}
}
Next we need the JAXB context and the unmarshaller itself:
private static final JAXBContext jaxbContext;
public Unmarshaller getUnmarshaller() {
try {
return jaxbContext.createUnmarshaller();
} catch (JAXBException ex) {
throw new IllegalStateException(ex);
}
}
Having that, the JAXB context is loading its candidates and checks if there is a match amongs them by the root element name. We just unmarshal and check whats the type of received object:
try {
Object unmarshalledObject = getUnmarshaller().unmarshal(new StringReader(xmlString));
if (unmarshalledObject instanceof Objecttype1) {
//do Objecttype1 related work
} else if (unmarshalledObject instanceof Objecttype2) {
//do Objecttype2 related work
} else {
// unexpected object type
}
} catch (JAXBException ex) {
//handle ex
}

Related

Error in converting XML containing attribute values to Java Object

I need to Unmarshal XML to Java Object, I have tried with below code but it is giving exception-
Main Class-
JAXBContext context = JAXBContext.newInstance(SimpleBean.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
File file = ResourceUtils.getFile("classpath:config/SimpleBean.xml");
SimpleBean param = (SimpleBean) unMarshaller.unmarshal(new FileInputStream(file));
LOGGER.info("param: "+param.getRoot());
SimpleBean.java
#JsonIgnoreProperties(ignoreUnknown=true)
public class SimpleBean {
#JsonProperty("root")
private Root root;
public Root getRoot() {
return root;
}
public void setRoot(Root root) {
this.root = root;
}
}
Root.java
public class Root {
#JsonProperty("Schedule")
private List<Schedule> schedule;
public List<Schedule> getSchedule() {
return schedule;
}
public void setSchedule(List<Schedule> schedule) {
this.schedule = schedule;
}
}
Schedule.java
#JsonIgnoreProperties(ignoreUnknown=true)
public class Schedule {
#JsonProperty("ID")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
SimpleBean.xml
<root>
<Schedule ID="561"></Schedule>
<Schedule ID="562"></Schedule>
</root>
Exception coming-
javax.xml.bind.UnmarshalException: unexpected element (uri:"",
local:"root"). Expected elements are (none)
You need to define an #XmlRootElement like this:
#XmlRootElement(name="root")
public class Root {
#JsonProperty("Schedule")
private List<Schedule> schedule;
public List<Schedule> getSchedule() {
return schedule;
}
public void setSchedule(List<Schedule> schedule) {
this.schedule = schedule;
}
}
Also, you don't need another wrapper POJO (SimpleBean).
You should do this directly (otherwise you'll get ClassCastException):
Root param = (Root) unMarshaller.unmarshal(new FileInputStream(file));
LOGGER.info("param: "+param);
This is a working solution-
SomeRoot.java-
#XmlRootElement(name="root")
public class SomeRoot{
private List<Schedule> schedule;
#XmlElement(name = "Schedule")
public List<Schedule> getSchedule() {
return schedule;
}
public void setSchedule(List<Schedule> schedule) {
this.schedule = schedule;
}
}
Schedule.java -
public class Schedule {
private String id;
#XmlAttribute(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
And then in java code-
JAXBContext context = JAXBContext.newInstance(SomeRoot.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
File file = ResourceUtils.getFile("classpath:config/SomeRoot.xml");
SomeRoot param = (SomeRoot) unMarshaller.unmarshal(file);
List<Schedule> schedules = param.getSchedule();
for (Schedule schedule : schedules) {
LOGGER.info("Schedule: "+schedule.getId());
}

Cannot delete an entity using morphia (mongodb)

I keep getting the same error whenever I try to delete an entity using Morphia:
org.mongodb.morphia.query.ValidationException: The field '_id' could not be found in ...
while validating - _id; if you wish to continue please disable validation.
I don't really want to disable validation. I just want to delete the object.
Here is my delete method of the service I'm writing:
public void delete(ObjectId id) {
BaseMaterial baseMaterial = this.findOne(id);
WriteResult writeResult = this.repo.delete(baseMaterial);
}
and here is the findOne method in the same service (i.e. this.findOne)
public BaseMaterial findOne(ObjectId id) {
Query<BaseMaterial> query = repo.createQuery(BaseMaterial.class);
return Optional.ofNullable(query.field("id").equal(id).get())
.orElseThrow(() -> new DataRetrievalFailureException(
"Failed to fetch " + this.getClass().getName() + " with id " + id.toString()));
}
And here is my POJO:
#Entity("baseMaterial")
public class BaseMaterial {
#Id
#NotNull
protected ObjectId id;
#NotEmpty
private String name;
private String description;
public String get_id() {
return this.id.toString();
}
#JsonIgnore
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The findOne method of shown above works as expected so I really have no idea why this is happening.
Any ideas?
I don't see a field annotated with #Id...

Error reading XML via JaxB

This is my class structure:
#XmlRootElement(name="System")
public class SystemDTO () {
#XmlElement(name="ID")
public void setId(String id) {
this.id = id;
}
#XmlElement(name="Source")
public void setSource(SourceDTO source) {
this.source = source;
}
}
#XmlRootElement(name="Source")
class SourceDTO {
#XmlElement(name="Name")
public void setName(String name) {
this.name = name;
}
}
This is my XML File:
<System>
<ID>e5b160d0</ID>
<Source>
<Name>Kron</Name>
</Source>
</System>
The problem is the Source is always null. I do not get a exception, it just comes out null. I've attempted to use just the Source tag and it picks up Name just fine, but when I add it as part of the System class it does not seem to work.
Additionally I attempted to do this and have a string member variable in System for name:
#XmlElementWrapper(name="Source")
#XmlElement(name="Name")
But that causes an exception. Any ideas?
The Test Class
public class JaxbTest {
public static void main(String[] args) {
String xml = "<System>\n" +
" <ID>e5b160d0</ID>\n" +
" <Source>\n" +
" <Name>Kron</Name>\n" +
" </Source>\n" +
"</System>";
SystemDTO systemDTO;
try {
JAXBContext jaxbContext = JAXBContext.newInstance(SystemDTO.class);
StringReader reader = new StringReader(xml);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
systemDTO = (SystemDTO) jaxbUnmarshaller.unmarshal(reader);
System.out.println(systemDTO.getSource().getName());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Source DTO
#XmlRootElement(name = "Source")
class SourceDTO {
private String name = null;
public String getName() {
return name;
}
#XmlElement(name = "Name")
public void setName(String name) {
this.name = name;
}
}
System DTO
#XmlRootElement(name = "System")
public class SystemDTO {
private String id;
private SourceDTO source;
public String getId() {
return id;
}
public SourceDTO getSource() {
return source;
}
#XmlElement(name = "ID")
public void setId(String id) {
this.id = id;
}
#XmlElement(name = "Source")
public void setSource(SourceDTO source) {
this.source = source;
}
}
One possible problem that I see is that you have declared 2 "Root" elements in your XML. Try #XmlType on your Source class:
#XmlType(name="Source")
class SourceDTO {
...
}

Adding attribute to xml element within JAXB API

I am using JAXB API for mapping a Java object to XML. My Java class is
#XmlRootElement(name = "ad")
#XmlAccessorType(XmlAccessType.FIELD)
class Item {
#XmlElement(name = "id", nillable = false)
#XmlCDATA
private int id;
#XmlElement(name = "url", nillable = false)
#XmlCDATA
private String url;
public Item() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
Output is like this :
<ad>
<id><![CDATA[ 104 ]]></id>
<url><![CDATA[www.google.com]]></url>
</ad>
I need to add an attribute to url element, for example :
<ad>
<id><![CDATA[ 104 ]]></id>
<url type="fr"><![CDATA[www.google.fr]]></url>
</ad>
I have tried many combinaisons using #XmlValue and #XmlAttribute ...
Your url variable should not be a String, but should be its own type. You should create a separate class for the url item, Url, and give it a String field, type, with an #XmlAttribute annotation.
For example,
#XmlRootElement(name = "ad")
#XmlAccessorType(XmlAccessType.FIELD)
class Item {
#XmlElement(name = "id")
private int id;
#XmlElement(name = "url")
private Url url;
public Item() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// #XmlAttribute
public Url getUrl() {
return url;
}
public void setUrl(Url url) {
this.url = url;
}
}
#XmlRootElement(name = "url")
#XmlAccessorType(XmlAccessType.FIELD)
class Url {
#XmlValue
private String value;
#XmlAttribute(name = "type")
private String type;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
Note that I don't have MOXY so I can't use or test your #XmlCDATA annotation.

How we can add filed as an attribute for an element for java REST API

How we can add field as an attribute for an element for java REST API
My Java class
#XmlRootElement(name = "rtml_application")
public class ApplicationsEntity extends BaseEntity {
private static final long serialVersionUID = 3256446889040622647L;
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private String path;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I want to generate xml response
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rtml_application>
<created_at type="datetime">2013-06-13T12:33:09.885+05:30</created_at>
<id type="integer">9</id>
<name type="string">REST TEST</name>
<path nil="true"></path>
</rtml_application>
could any please help me out fix this problem using java annotation with jersey.
#XmlAttribute designates a field as an attribute, rather than an element

Categories