UnMarshalling XML gives null java objects.
XML :
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item Name="John"/>
<Item Name="Jason"/>
</Items>
Items class :
#XmlRootElement(name = "Items")
#XmlAccessorType(XmlAccessType.FIELD)
public class Items{
#XmlElement
private List<Item> item;
public List<Item> getItem() {
return this.Item;
}
}
Item class :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Item")
public class Item{
#XmlAttribute
private String name;
public String getName() {
return this.Name;
}
}
Java Code that UnMarshalls : Here result.getBody gives XML String
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
JAXBContext jaxbContext = JAXBContext.newInstance(Items.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Items itResult = (Items) jaxbUnmarshaller.unmarshal(new StringReader(result.getBody()));
Item object always comes null. How to unmarshall the xml correctly ?
Thanks in Advance. :
Use the following classes :
Item.java
#XmlRootElement(name = "Item")
public class Item
{
private String name;
public String getName()
{
return this.name;
}
#XmlAttribute(name = "Name" )
public void setName( String name )
{
this.name = name;
}
}
Items.java
#XmlRootElement(name = "Items")
public class Items
{
#XmlElementWrapper( name = "Items")
private List<Item> items = new ArrayList<Item>();
public List<Item> getItemList()
{
return this.items;
}
#XmlElement(name = "Item")
public void setItemList( List<Item> items )
{
this.items = items;
}
}
Test.java
public class Test
{
public static void main( String... args )
{
try
{
JAXBContext jaxbContext = JAXBContext.newInstance( Items.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Items itResult = (Items) jaxbUnmarshaller.unmarshal( new File( "Items.xml" ) );
if ( itResult != null )
{
List<Item> items = itResult.getItemList();
for ( Item item : items )
{
System.out.println( item.getName() );
}
}
}
catch ( Exception e )
{
e.printStackTrace();
}
}
}
You should get the Items object which contains Item s.
Changes I made:
a) You need an #XmlElementWrapper on the list which says that the Items is a wrapper around Items.
b) Move the #XmlAttribute to setter in Item.java
c) Move the #XmlElement to setter in Items.java
Item Class:
public class Item {
private String Name;
}
Items Class:
#XmlRootElement(name = "Items")
#XmlAccessorType(XmlAccessType.FIELD)
public class Items {
#XmlElement(name = "Item")
private List<Item> items;
}
Unmarshall class:
JAXBContext jaxbContext;
Unmarshaller jaxbUnmarshaller;
try {
jaxbContext = JAXBContext.newInstance(Items.class);
jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Items itResult = (Items) jaxbUnmarshaller.unmarshal(file);
System.out.println(itResult);
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
You missed some (name = "...") in your JAXB annotations,
and therefore you got some default names not matching your XML contents.
And that's why these fields remained null when unmarshalling your XML.
Items.java
For field item you need to explicitly set (name = "Item").
Otherwise you would get the default name "item", which is wrong.
#XmlRootElement(name = "Items")
#XmlAccessorType(XmlAccessType.FIELD)
public class Items {
#XmlElement(name = "Item")
private List<Item> item;
// ... getter
}
Item.java
For field name you need to explicitly set (name = "Name").
Otherwise you would get the default name "name", which is wrong.
And by the way, you don't need #XmlRootElement here,
because it has no effect on non-root elements.
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
#XmlAttribute(name = "Name")
private String name;
// ... getter
}
Related
I'm trying to integrate with some provider REST API within xml. I have two requests and two very similar responses: Operation response and Check response.
Operation:
<Response>
<ReturnCode>0</ReturnCode>
<ReturnMessage>OK</ReturnMessage>
<Commands>
<OperationResponseCommand>
<ResultCode>412</ResultCode>
<ResultMessage>Some message hear</ResultMessage>
<OperationId>125206188472552900</OperationId>
<Id>14507921</Id>
</OperationResponseCommand>
</Commands>
</Response>
Check:
<Response>
<ReturnCode>0</ReturnCode>
<ReturnMessage>OK</ReturnMessage>
<Commands>
<CheckResponseCommand>
<ResultCode>412</ResultCode>
<ResultMessage>Some message hear</ResultMessage>
<OperationId>125206188472552900</OperationId>
</CheckResponseCommand>
</Commands>
</Response>
As you can see, the differences are:
<OperationResponseCommand> instead of <CheckResponseCommand>
in first xml.
One additional tag <Id> inside
<OperationResponseCommand>.
And I have two classes for Response and for Command.
First:
#Data
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name = "ReturnCode")
private Integer returnCode;
#XmlElement(name = "ReturnMessage")
private String returnMessage;
#XmlElementWrapper(name = "Commands")
#XmlElement(name = "OperationResponseCommand")
private List<Command> commands = new ArrayList<>();
}
Second:
#Data
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Command {
#XmlElement(name = "ResultCode")
private Integer resultCode;
#XmlElement(name = "ResultMessage")
private String resultMessage;
#XmlElement(name = "OperationId")
private String operationId;
#XmlElement(name = "Id")
private Integer id;
}
For building response object from xml I'm using this class:
public class JAXBOperations {
public <T> T buildObjectFromXml(Class<T> clazz, String xml) {
try {
StringReader reader = new StringReader(xml);
Unmarshaller unmarshaller = createUnmarshaller(clazz);
return (T) unmarshaller.unmarshal(reader);
} catch (JAXBException e) {
throw new RuntimeException("Exception has been occurred while creating unmarshaller to parse xml to object");
}
}
public <T> String buildXmlFromObject(T objectToXml) {
try {
StringWriter writer = new StringWriter();
Marshaller marshaller = createMarshaller(objectToXml.getClass());
marshaller.marshal(objectToXml, writer);
return writer.toString();
} catch (JAXBException e) {
throw new RuntimeException("Exception has been occurred while creating marshaller to parse object to xml");
}
}
private <T> Marshaller createMarshaller(Class<T> clazz) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
return jaxbContext.createMarshaller();
}
private <T> Unmarshaller createUnmarshaller(Class<T> clazz) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
return jaxbContext.createUnmarshaller();
}
}
Method buildObjectFromXml. And it works fine for first xml response. I have an object like this:
Response(
returnCode=0, returnMessage=OK,
commands=[Command(
resultCode=417,
resultMessage=Processing,
operationId=b6619f26-8583-4272-b89d-d1b200109d06,
id=157427079)])
But for the second xml response it doesn't works. I have:
Response(
returnCode=0,
returnMessage=OK,
commands=[])
I tried to change the code of my Response class. Like this:
#Data
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class tResponse {
#XmlElement(name = "ReturnCode")
private Integer returnCode;
#XmlElement(name = "ReturnMessage")
private String returnMessage;
#XmlElementWrapper(name = "Commands")
#XmlElementRefs({
#XmlElementRef(name = "OperationResponseCommand", type = Command.class),
#XmlElementRef(name = "CheckResponseCommand", type = Command.class)})
private List<Command> commands = new ArrayList<>();
}
Or this:
#Data
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name = "ReturnCode")
private Integer returnCode;
#XmlElement(name = "ReturnMessage")
private String returnMessage;
#XmlElementWrapper(name = "Commands")
#XmlElement(name = "OperationResponseCommand")
private List<Command> operstionCommands = new ArrayList<>();
#XmlElementWrapper(name = "Commands")
#XmlElement(name = "CheckResponseCommand")
private List<Command> checkCommands = new ArrayList<>()
}
But it both doesn't works too.
My question is: what I'm doing wrong? How can I change code of my two classes to correct converting to object both types of xml responses?
For representing the XML elements <OperationResponseCommand>
and <CheckResponseCommand> you will need separate classes
which should be subclasses of your Command classes.
Let's call them OperationResponseCommand and CheckResponseCommand.
The properties common to all commands should remain in your
Command class. It is also a good idea to declare this class as abstract.
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class Command {
#XmlElement(name = "ResultCode")
private Integer resultCode;
#XmlElement(name = "ResultMessage")
private String resultMessage;
#XmlElement(name = "OperationId")
private Integer operationId;
}
The properties specific for the commands should go into the subclasses:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class OperationResponseCommand extends Command {
#XmlElement(name = "Id")
private Integer id;
}
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class CheckResponseCommand extends Command {
}
Finally, in your Response class you need to enhance the annotations
of your List<Command> commands property, so that JAXB
will know which object class needs to be instantiated depending
on the XML element name of the commands.
(See also the javadoc of #XmlElements.)
#XmlElementWrapper(name = "Commands")
#XmlElements({
#XmlElement(name = "OperationResponseCommand", type = OperationResponseCommand.class),
#XmlElement(name = "CheckResponseCommand", type = CheckResponseCommand.class)
})
private List<Command> commands = new ArrayList<>();
How do I annotate the externalValue and companyId fields in the Root class so that "abc" gets mapped to externalValue and "123" gets mapped to companyId?
Do I need the #XmlJavaTypeAdapter annotation? Where? I'm hoping that if I do, it can just handle those 2 fields and I can leave the annotations for title and countryCodes as-is.
XML:
<item>
<externalValue companyId="123">abc</externalValue>
<title>My Title</title>
<country>US</country>
<country>CA</country>
</item>
POJO:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private String externalValue;
private String companyId;
#XmlElement
private String title;
#XmlElement(name = "country")
public List<String> countryCodes;
// getters and setters...
}
I am afraid that this is not possible to achieve only with annotations (so without extra POJO and some adapter) in general case namely JAXB specs. However if your happen to use MOXy as your JAXB implementation it is easy as adding annotation #XmlPath like this:
#XmlPath("externalValue/#companyId")
private String companyId;
Related question: Unmarshalling an XML using Xpath expression and jaxb
You have to define the class in the following manner.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
private CompanyIdValue companyIdValue;
#XmlElement
private String title;
#XmlElement(name = "country")
public List<String> countryCodes;
//getter and setter
}
In case of both attribute in an XML element tag, you have to define a separate class. Define a separate class called CompanyIdValue, for XML element, you have to define #XmlValue and for attribute you have to annotate with #XmlAttribute
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
public class CompanyIdValue {
#XmlElement(name = "externalValue")
private String externalValue;
private String companyId;
public String getExternalValue() {
return externalValue;
}
#XmlValue
public void setExternalValue(String externalValue) {
this.externalValue = externalValue;
}
public String getCompanyId() {
return companyId;
}
#XmlAttribute
public void setCompanyId(String companyId) {
this.companyId = companyId;
}
}
I provide below a test program also for testing.
public class Test {
public static void main(String[] args) {
try {
Item item = new Item();
CompanyIdValue companyIdValue = new CompanyIdValue();
companyIdValue.setCompanyId("SomeId");
companyIdValue.setExternalValue("Some External value");
item.setCompanyIdValue(companyIdValue);
item.setCountryCodes(Arrays.asList("A", "B"));
item.setTitle("Some Title");
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(item, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
I am trying to unmarshall this XML to Java objects, a Customer object containing a List of EmailAdresses.
<customer>
<emailAddresses>janed#example.com</emailAddresses>
<emailAddresses>jdoe#example.org</emailAddresses>
</customer>
Having an issue with the list, I get the correct number of list items (2), but the value of the emailAddresses tag is null
Customer.java
#XmlRootElement( name = "customer" )
public class Customer
{
private List<EmailAddress> emailAddresses;
public Customer()
{
emailAddresses = new ArrayList<EmailAddress>();
}
public List<EmailAddress> getEmailAddresses()
{
return emailAddresses;
}
public void setEmailAddresses( List<EmailAddress> emailAddresses )
{
this.emailAddresses = emailAddresses;
}
}
EmailAddress.java
public class EmailAddress
{
private String emailAddresses;
public String getEmailAddresses()
{
return emailAddresses;
}
public void setEmailAddresses( String emailAddresses )
{
this.emailAddresses = emailAddresses;
}
}
Failing Unit Test
#Test
public void shouldDeserialiseCusomerXMLToObject() throws JAXBException
{
String xml = "<customer>"
+ " <emailAddresses>janed#example.com</emailAddresses>"
+ " <emailAddresses>jdoe#example.org</emailAddresses>"
+ "</customer>";
JAXBContext jaxbContext = JAXBContext.newInstance( Customer.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader( xml );
Customer msg = ( Customer ) jaxbUnmarshaller.unmarshal( reader );
// This passes, I have 2 emailAddresses
assertEquals( 2, msg.getEmailAddresses().size() );
// This fails, I have a null pointer instead of the email address
assertEquals( "janed#example.com", msg.getEmailAddresses().get( 0 ).getEmailAddresses() );
}
The emailAddresses field of EmailAddress is by default treated as a subelement, expecting the XML to be:
<customer>
<emailAddresses>
<emailAddresses>janed#example.com</emailAddresses>
</emailAddresses>
<emailAddresses>
<emailAddresses>jdoe#example.org</emailAddresses>
</emailAddresses>
</customer>
Since your outer <emailAddresses> element doesn't contain an inner <emailAddresses> element, the field is never assigned.
You want the emailAddresses field of EmailAddress to be the value of the (outer) <emailAddresses> element, so you have to tell JAXB that, by specifying the #XmlValue annotation:
#XmlValue
public String getEmailAddresses()
{
return emailAddresses;
}
The #XmlValue annotation is especially useful when combined with #XmlAttribute, to support XML like this:
<Person sex="male" age="25">John Doe</Person>
Where class would then be:
public class Person {
public enum Sex {
#XmlEnumValue("male") MALE,
#XmlEnumValue("female") FEMALE,
}
#XmlAttribute
private Sex sex;
#XmlAttribute
private int age;
#Value
private String name;
}
You have a level too many of email addresses.
If you should change the list of email addresses to a list of strings, like
#XmlRootElement( name = "customer" )
public class Customer
{
private List<String> emailAddresses;
public Customer()
{
emailAddresses = new ArrayList<String>();
}
public List<String> getEmailAddresses()
{
return emailAddresses;
}
public void setEmailAddresses( List<String> emailAddresses )
{
this.emailAddresses = emailAddresses;
}
}
I have an XML to unmarshall with JAXB. The code works fine if I remove all namespace attributes from the elements but I get a null object after unmarshalling if I keep the namespace attributes.
The XML is like this:
<Animal xmlns="http://allmycats.com/serviceplatform/1.0/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Cat z:Id="i3" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<name>kitty</name>
</Cat>
<Cat z:Id="i2" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<name>kitty2</name>
</Cat>
</Animal>
My Animal bean is like this:
#XmlRootElement(name = "Animal")
public class Animal{
List<Cat> cats;
#XmlElement(name = "Cat")
public List<Cat> getCats() {
return cats;
}
public void setCats(List<Cat>cats) {
this.cats= cats;
}
}
The Cats bean is like:
#XmlRootElement(name = "Cat")
public class Cat {
private String zId;
#XmlAttribute(name = "z:Id", namespace="http://schemas.microsoft.com/2003/10/Serialization/")
public String getzId() {
return zId;
}
public void setzId(String zId) {
this.zId = zId;
}
private String name;
#XmlElement(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
At runtime, I get an empty object. I tried to remove "z:" from the attribute and I got this exception:
org.xml.sax.SAXParseException; The prefix "z" for attribute "z:Id" associated with an element type "Cat" is not bound.]
If I remove namespaces from cat and Animal, I get this exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://allmycats.com/serviceplatform/1.0/", local:"Animal"). Expected elements are <{}Animal>
Final code to unmarshall is below. The last line gives a null pointer exception
File file = new File(filepath1);
System.out.println("file exists? : "+ file.exists()); // prints true
JAXBContext jaxbContext = JAXBContext.newInstance(Animal2.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Animal2 animals = (Animal2)jaxbUnmarshaller.unmarshal( file );
System.out.println("--file size: "+animals.getCats().size());
I am not sure how to handle the namespaces and the z: attributes in my POJO classes. Any suggestions please ?
The code works fine if I do not have any namespace or any attribute with namespace in the XML but I cannot alter the XML.
XMLAtribute has atribute namesape, so
#XmlAttribute(name = "Id", namespace="http://schemas.microsoft.com/2003/10/Serialization").
While judging by your xml, the cat is in the same namespace as the animal so
The following code works with JDK 7 (fixed the ns for animal and attribute name for zid).
#XmlRootElement(name = "Animal",namespace = "http://allmycats.com/serviceplatform/1.0/")
public class Animal2{
List<Cat2> cats;
#XmlElement(name = "Cat")
public List<Cat2> getCats() {
return cats;
}
public void setCats(List<Cat2>cats) {
this.cats= cats;
}
}
#XmlRootElement(name = "Cat")
public class Cat2 {
private String zId;
#XmlAttribute(name = "Id", namespace="http://schemas.microsoft.com/2003/10/Serialization/")
public String getzId() {
return zId;
}
public void setzId(String zId) {
this.zId = zId;
}
private String name;
#XmlElement(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I wrote this custom IDResolver:
final class MyIDResolver extends IDResolver {
Map<String, Course> courses = new HashMap<>();
/**
* #see com.sun.xml.bind.IDResolver#bind(java.lang.String, java.lang.Object)
*/
#Override
public void bind(String id, Object obj) throws SAXException {
if (obj instanceof Course)
courses.put(id, (Course)obj);
else
throw new SAXException("This " + obj.toString() + " cannot found");
}
/**
* #see com.sun.xml.bind.IDResolver#resolve(java.lang.String, java.lang.Class)
*/
#Override
public Callable<?> resolve(final String id, final Class targetType) throws SAXException {
return new Callable<Object>() {
#Override
public Object call() throws Exception {
if (targetType == Course.class)
return courses.get(id);
else
throw new ClassNotFoundException(targetType.toString() + " cannot found");
}
};
}
}
And I have two classes like:
#XmlRootElement(name = "course")
public class Course {
#XmlID
#XmlAttribute
private String id;
#XmlAttribute
private int units;
#XmlAttribute
private Level level;
#XmlAttribute
private String name;
#XmlIDREF
#XmlElement(name = "pre")
private ArrayList<Course> prerequisite;
#XmlIDREF
#XmlElement(name = "co")
private ArrayList<Course> corequisite;
}
#XmlRootElement(name = "dept")
#XmlAccessorType(XmlAccessType.FIELD)
public final class Department {
#XmlAttribute
private String name;
#XmlElementWrapper(name = "courses")
#XmlElement(name = "course")
private ArrayList<Course> courses;
}
And sample XML like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<dept name="ece">
<courses>
<course id="1" units="4" level="UNDERGRADUATE" name="Fundamentals of Programming"/>
<course id="2" units="3" level="UNDERGRADUATE" name="Advanced Programming">
<pre>1</pre>
</course>
</courses>
</dept>
And a simple main like this:
unmarshaller.unmarshal(new FileReader(arg0))
context = JAXBContext.newInstance(Department.class);
unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty(IDResolver.class.getName(), new MyIDResolver());
The resolve() method of a custom IDResolver gets Object.class as targetType. But it should be Course.class. This results in wrong ID resolving. Where is my problem?
You should not use IDResolve when Department contains course. It should be used when Department want to reference to a Course. So for this reason when jaxb arrives a course inside of a department that it contains an element (here <pre>) it will pass object.class to your resolve function.