#XmlRegistry - how does it work? - java

I have found some examples of JAXB2 #XmlRegistry over the internet but no good in-depth tutorials that talk about the concept of using #XmlRegistry with #XmlElementDecl, wonder if its a concept not much explored in general.
Anyways here is my question, first some sample classes that I am using to unmarshall an xml using JAXB:
The main class I am trying to unmarshal using JAXB - Employee.java
package com.test.jaxb;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import com.test.jaxb.dto.Address;
#XmlRootElement
public class Employee {
private int id;
private String name;
private String email;
private List<Address> addresses;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
#SuppressWarnings("unused")
#XmlRegistry
public static class XMLObjectFactory {
#XmlElementDecl(scope = Employee.class, name= "id")
JAXBElement<String> createEmployeeId(String value) {
return new JAXBElement<String>(new QName("id"), String.class, "100");
}
#XmlElementDecl(scope = Employee.class, name= "name")
JAXBElement<String> createName(String value) {
return new JAXBElement<String>(new QName("name"), String.class, "Fake Name");
}
#XmlElementDecl(scope = Employee.class, name= "email")
JAXBElement<String> createEmail(String value) {
return new JAXBElement<String>(new QName("email"), String.class, value);
}
#XmlElementDecl(scope = Employee.class, name= "addresses")
JAXBElement<List> createAddresses(List value) {
return new JAXBElement<List>(new QName("addresses"), List.class, value);
}
}
}
The child class - Address.java
package com.test.jaxb.dto;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.namespace.QName;
import com.test.jaxb.Employee;
#XmlRootElement
public class Address {
private String addressLine1;
private String addressLine2;
private String addressLine3;
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
public String getAddressLine3() {
return addressLine3;
}
public void setAddressLine3(String addressLine3) {
this.addressLine3 = addressLine3;
}
#SuppressWarnings("unused")
#XmlRegistry
private static class XMLObjectFactory {
#XmlElementDecl(scope = Employee.class, name= "addressLine1")
JAXBElement<String> createAddressLine1(String value) {
return new JAXBElement<String>(new QName("addressLine1"), String.class, value);
}
#XmlElementDecl(scope = Employee.class, name= "addressLine2")
JAXBElement<String> createAddressLine2(String value) {
return new JAXBElement<String>(new QName("addressLine2"), String.class, value);
}
#XmlElementDecl(scope = Employee.class, name= "addressLine3")
JAXBElement<String> createAddressLine3(String value) {
return new JAXBElement<String>(new QName("addressLine3"), String.class, value);
}
}
}
The xml to be unmarshalled - employee.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>1</id>
<name>Vaishali</name>
<email>Vaishali#example.com</email>
<addresses>
<address>
<addressLine1>300</addressLine1>
<addressLine2>Mumbai</addressLine2>
<addressLine3>India</addressLine3>
</address>
<address>
<addressLine1>301</addressLine1>
<addressLine2>Pune</addressLine2>
<addressLine3>India</addressLine3>
</address>
</addresses>
</employee>
Unmarshalling Code :
package com.test.jaxb;
import java.io.FileReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class ObjectFactoryTest {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("resources/employee.xml");
JAXBContext context = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(reader);
System.out.println(obj);
}
}
When I unmarshal the employee xml using above code, the address list does not get populated. The resulting employee object only has a blank list of adresses. Is there anything wrong with my mappings?
To find out what is going on and see if the employee objects are actually being created using the Object Factory (having the #XMLRegistry annotation), I changed the value of id and name in factory methods, however that had no effect in the output, which tells me JAXB is not actually using the ObjectFactory, why?
Am I going aboout this all wrong? Any help would be appreciated.

#XmlRegistry - how does it work?
#XmlRegistry is used to mark a class that has #XmlElementDecl annotations. To have your JAXB implementation process this class you need to ensure that it is included in the list of classes used to bootstrap the JAXBContext. It is not enough for it to be a static inner class of one of your domain model classes:
JAXBContext context = JAXBContext.newInstance(Employee.class, Employee.XMLObjectFactory.class);
#XmlElementDecl - how does it work?
If the value of the field/property is going to be a JAXBElement then you need to leverage #XmlElementDecl. A JAXBElement captures information that is can be useful:
Element name, this is necessary if you are mapping to a choice structure where multiple elements are of the same type. If the element name does not correspond to a unique type then you would not be able to round-trip the document.
JAXBElement can be used to represent an element with xsi:nil="true".
XmlObjectFactory
#XmlElementDecl also allows you to specify a scope. I have modified the model from you post a bit. I have introduced an XmlObjectFactory class that has two #XmlElementDecl. Both specify a name of address. I have leveraged the scope property so that for properties within the Employee class the #XmlElementDecl corresponding to the Address class with be used.
package forum11078850;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class XmlObjectFactory {
#XmlElementDecl(scope = Employee.class, name = "address")
JAXBElement<Address> createAddress(Address value) {
return new JAXBElement<Address>(new QName("address"), Address.class, value);
}
#XmlElementDecl(name = "address")
JAXBElement<String> createStringAddress(String value) {
return new JAXBElement<String>(new QName("address"), String.class, value);
}
}
Employee
The #XmlElementRef annotation will cause the value of the property to be matched on its root element name. Possible matches will include classes mapped with #XmlRootElement or #XmlElementDecl.
package forum11078850;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder = { "id", "name", "email", "addresses" })
public class Employee {
private int id;
private String name;
private String email;
private List<JAXBElement<Address>> addresses;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#XmlElementWrapper
#XmlElementRef(name="address")
public List<JAXBElement<Address>> getAddresses() {
return addresses;
}
public void setAddresses(List<JAXBElement<Address>> addresses) {
this.addresses = addresses;
}
}
ObjectFactoryTest
package forum11078850;
import java.io.FileReader;
import javax.xml.bind.*;
public class ObjectFactoryTest {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("src/forum11078850/input.xml");
JAXBContext context = JAXBContext.newInstance(Employee.class, XmlObjectFactory.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(reader);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(obj, System.out);
}
}
The Address class and input.xml from my original answer can be used to run this example.
ORIGINAL ANSWER
I'm not sure how you are attempting to use #XmlRegistry, so I will focus on the following part of your post:
When I unmarshal the employee xml using above code, the address list
does not get populated. The resulting employee object only has a blank
list of adresses. Is there anything wrong with my mappings?
Your list of Address objects is wrapped in a grouping element (addresses), so you need to use the #XmlElementWrapper annotation to map this use case. Below is a complete example:
Employee
package forum11078850;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlType(propOrder = { "id", "name", "email", "addresses" })
public class Employee {
private int id;
private String name;
private String email;
private List<Address> addresses;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#XmlElementWrapper
#XmlElement(name = "address")
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
}
Address
package forum11078850;
public class Address {
private String addressLine1;
private String addressLine2;
private String addressLine3;
public String getAddressLine1() {
return addressLine1;
}
public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}
public String getAddressLine2() {
return addressLine2;
}
public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}
public String getAddressLine3() {
return addressLine3;
}
public void setAddressLine3(String addressLine3) {
this.addressLine3 = addressLine3;
}
}
ObjectFactoryTest
package forum11078850;
import java.io.FileReader;
import javax.xml.bind.*;
public class ObjectFactoryTest {
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("src/forum11078850/input.xml");
JAXBContext context = JAXBContext.newInstance(Employee.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object obj = unmarshaller.unmarshal(reader);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(obj, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<id>1</id>
<name>Vaishali</name>
<email>Vaishali#example.com</email>
<addresses>
<address>
<addressLine1>300</addressLine1>
<addressLine2>Mumbai</addressLine2>
<addressLine3>India</addressLine3>
</address>
<address>
<addressLine1>301</addressLine1>
<addressLine2>Pune</addressLine2>
<addressLine3>India</addressLine3>
</address>
</addresses>
</employee>

You have to take a List object of Address. In that object, you will have to add the object which contains data like addressline1. addressline2 and so on.
i.e.
List addrObjList = new List();
addrObjList.add(object); // Bind an object containing data and add one by one

Related

Why am i getting null in java from xml even though value is present in xml?

Im trying to build java object for xml by
java code
JAXBContext jaxbContext = JAXBContext.newInstance(Enfinity.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Enfinity enfinity = (Enfinity) jaxbUnmarshaller.unmarshal(xmlFile);
xml
<?xml version="1.0" encoding="UTF-8"?>
<enfinity xmlns="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dt="http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt" xsi:schemaLocation="http://www.intershop.com/xml/ns/intershop/customer/impex/7.3 b2b_customer.xsd http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt dt.xsd" major="6" minor="1" family="enfinity" branch="enterprise" build="1.0.299">
<customer id="34627">
<customer-type>SMB</customer-type>
<company-name>xxxxx & xxxxx</company-name>
<industry>Retail</industry>
<enabled>1</enabled>
<approval-status>1</approval-status>
<custom-attributes>
<custom-attribute name="CustomerPriceLevel" dt:dt="string">4</custom-attribute>
<custom-attribute name="FreeFreightThreshold" dt:dt="string">300.00</custom-attribute>
<custom-attribute name="ECOMCustomerId" dt:dt="string">xxxxxx</custom-attribute>
<custom-attribute name="BlockCreditCardPayment" dt:dt="boolean">true</custom-attribute>
<custom-attribute name="CustomLoad" dt:dt="string">true</custom-attribute>
</custom-attributes>
<users>
<user business-partner-no="xxxx">
<business-partner-no>xxxxx</business-partner-no>
<profile>
<first-name>xxxx</first-name>
<last-name>xxxx</last-name>
<email>xxx</email>
</profile>
<user-groups>
<user-group id="IG_SMBCustomers"/>
<user-group id="IG_RecurringUsers"/>
<user-group id="52"/>
</user-groups>
</user>
<user business-partner-no="xxxxx">
<business-partner-no>xxxxx</business-partner-no>
<profile>
<credentials>
<login>xxxxx.com</login>
<password encrypted="1">xxxxx</password>
<enabled>1</enabled>
<reminder-email>xxxxxx.com</reminder-email>
<password-creation-date>2019-03-28T06:37:29-07:00</password-creation-date>
</credentials>
<creation-date>2019-02-28T03:45:43-08:00</creation-date>
<phone-home>xxxxxx</phone-home>
<email>xxxxxxxxx.com</email>
<last-name>xxxxx</last-name>
<first-name>xxxxxx</first-name>
<custom-attributes>
<custom-attribute name="RoleID" dt:dt="string">APP_B2B_BUYER</custom-attribute>
</custom-attributes>
</profile>
</user>
</users>
when I try to get login from user tag even though value is there im getting null my poji looks like
Customer
package com.poc.highline;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "customer")
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
#XmlElement (name = "id")
String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement (name = "customer-type")
String customerType;
public String getCustomerType() {
return customerType;
}
public void setCustomerType(String customerType) {
this.customerType = customerType;
}
#XmlElement (name = "company-name")
String companyName;
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
#XmlElement (name = "industry")
String industry;
public String getIndustry() {
return industry;
}
public void setIndustry(String industry) {
this.industry = industry;
}
#XmlElement (name = "enabled")
String enabled;
public String getEnabled() {
return enabled;
}
public void setEnabled(String enabled) {
this.enabled = enabled;
}
#XmlElement (name = "approval-status")
String approvalStatus;
public String getApprovalStatus() {
return approvalStatus;
}
public void setApprovalStatus(String approvalStatus) {
this.approvalStatus = approvalStatus;
}
#XmlElement (name = "custom-attributes")
List<customAttributes> customAttributes;
public List<customAttributes> getCustomAttributes() {
return customAttributes;
}
public void setCustomAttributes(List<customAttributes> customAttributes) {
this.customAttributes = customAttributes;
}
#XmlElement (name = "users")
List<Users> users;
public List<Users> getUsers() {
return users;
}
public void setUsers(List<Users> users) {
this.users = users;
}
#XmlElement (name = "preferred-invoice-to-address")
PreferredInvoiceAddress invoiceAddress;
public PreferredInvoiceAddress getInvoiceAddress() {
return invoiceAddress;
}
public void setInvoiceAddress(PreferredInvoiceAddress invoiceAddress) {
this.invoiceAddress = invoiceAddress;
}
#XmlElement (name = "addresses")
List<Addresses> addresses;
public List<Addresses> getAddresses() {
return addresses;
}
public void setAddresses(List<Addresses> addresses) {
this.addresses = addresses;
}
}
This is users POJO class
Users
package com.poc.highline;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "users")
#XmlAccessorType(XmlAccessType.FIELD)
public class Users {
#XmlElement(name="user")
List<User> user;
public List<User> getUser() {
return user;
}
public void setUser(List<User> user) {
this.user = user;
}
}
this is the user POJO class
user
package com.poc.highline;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "user")
#XmlAccessorType(XmlAccessType.FIELD)
public class User {
#XmlElement(name = "business-partner-no")
String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlElement(name = "profile")
String profile;
public String getProfile() {
return profile;
}
public void setProfile(String profile) {
this.profile = profile;
}
#XmlElement(name = "credentials")
String credentials;
public String getCredentials() {
return credentials;
}
public void setCredentials(String credentials) {
this.credentials = credentials;
}
String login;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
String email;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Im getting value in id but not in other variable refer the watchList
enfinity Enfinity (id=30)
customer ArrayList<E> (id=32)
[0] Customer (id=43)
addresses ArrayList<E> (id=45)
approvalStatus "1" (id=46)
companyName "xxxxx & xxxxx" (id=49)
customAttributes ArrayList<E> (id=50)
customerType "SMB" (id=51)
enabled "1" (id=52)
id null
industry "Retail" (id=53)
invoiceAddress PreferredInvoiceAddress (id=54)
users ArrayList<E> (id=56)
[0] Users (id=58)
user ArrayList<E> (id=60)
[0] User (id=62)
credentials null
email null
id "xxxxx" (id=65)
login null
profile "\n " (id=66)
[1] User (id=63)
credentials null
email null
id "xxxxx" (id=67)
login null
profile "\n " (id=68)
please help thanks in advance.
You are getting null for the Id of your customer because it's not an #XmlElement. Use #XmlAttribute instead.
For clarification:
<customer id="1">
<name>somename</name>
<customer>
id: #XmlAttribute
name: #XmlElement
somename: #XmlValue
Check https://howtodoinjava.com/jaxb/jaxb-annotations/ or other Jaxb tutorials for other annotations.
Edit:
For login and email, you have to annotate them with #XmlAttribute as well.

How to get a value between tags with XML format using jaxb

I have a xml format like below:
<list>12
<item name="a">
<child parent="b" age="1">David Beckham</child>
</item>
</list>
The question is how could i extract 12 using JAXB? Which annotation supports it? Thank a lot.
Following is the working example.
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.transform.stream.StreamSource;
public class TestJAXBMixed {
public static void main(String[] args) throws Exception {
final String str =
"<list>12<item name=\"a\"><child parent=\"b\" age=\"1\">David Beckham</child></item></list>";
final JAXBContext jaxbContext = JAXBContext.newInstance(Child.class, Item.class, ListElement.class);
final JAXBElement<ListElement> obj = jaxbContext.createUnmarshaller().unmarshal(new StreamSource(new StringReader(str)), ListElement.class);
// The following is an array of objects representing the <list> content in order,
// i.e. it is a sequence of a String (12) and an Object (Item)
final List<Object> content = obj.getValue().getContent();
System.out.println(content);
}
}
#XmlRootElement(name="child")
class Child {
private String parent;
private String age;
private String text;
public String getParent() {
return parent;
}
#XmlAttribute(name="parent")
public void setParent(String parent) {
this.parent = parent;
}
public String getAge() {
return age;
}
#XmlAttribute(name="age")
public void setAge(String age) {
this.age = age;
}
public String getText() {
return text;
}
#XmlValue
public void setText(String text) {
this.text = text;
}
}
#XmlRootElement(name="item")
class Item {
private String name;
private Child child;
public String getName() {
return name;
}
#XmlAttribute(name="name")
public void setName(String name) {
this.name = name;
}
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
#XmlRootElement(name="list")
class ListElement {
private List<Object> content;
public List<Object> getContent() {
return content;
}
#XmlMixed
#XmlElementRefs({
#XmlElementRef(name="item", type=Item.class)
})
public void setContent(List<Object> content) {
this.content = content;
}
}
SimpleXml can do it:
final String data = ...
final SimpleXml simple = new SimpleXml();
final Element element = simple.fromXml(data);
System.out.println(element.text);
Will output:
12
From maven central:
<dependency>
<groupId>com.github.codemonstur</groupId>
<artifactId>simplexml</artifactId>
<version>1.4.0</version>
</dependency>

multi instance xml file ummarshalling to objects

I am using JAXB to covert xml to objects. After doing some tutorial, I can successfully convert a simple xml (which has single object and unique element tag) to object. Then I need to deal with more complicated xml which has multiple instances and one parent tag. I still use the similar structure. But I couldn't get the expected output of three country objects. what is wrong with my code? Please help.
IntelliJ IDE console output is:
Countries#5e20a82a
Process finished with exit code 0
xml file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Countries>
<Country>
<Country_Name>Spain</Country_Name>
<Country_Capital>Madrid</Country_Capital>
<Country_Continent>Europe</Country_Continent>
</Country>
<Country>
<Country_Name>USA</Country_Name>
<Country_Capital>Washington</Country_Capital>
<Country_Continent>America</Country_Continent>
</Country>
<Country>
<Country_Name>Japan</Country_Name>
<Country_Capital>Tokyo</Country_Capital>
<Country_Continent>Asia</Country_Continent>
</Country>
</Countries>
Countries.java
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlType( propOrder = { "name", "capital", "foundation", "continent" , "population"} )
#XmlRootElement( name = "Countries" )
public class Countries {
private int population;
private String name;
private String capital;
private int importance;
private String foundation;
private String continent;
#XmlElement(name = "Country_Population")
public void setPopulation(int population) {
this.population = population;
}
#XmlElement(name = "Country_Name")
public void setName(String name) {
this.name = name;
}
#XmlElement(name = "Country_Capital")
public void setCapital(String capital) {
this.capital = capital;
}
#XmlAttribute(name = "importance", required = true)
public void setImportance(int importance) {
this.importance = importance;
}
#XmlElement(name = "Country_foundation")
public void setFoundation(String foundation) {
this.foundation = foundation;
}
#XmlElement(name = "Country_Continent")
public void setContinent(String continent) {
this.continent = continent;
}
}
CountryReader.java
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
public class CountryReader {
public static void main(String[] args) throws JAXBException {
File file = new File("country.xml");
JAXBContext jaxbContext = JAXBContext.newInstance( Countries.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Countries countres = (Countries)jaxbUnmarshaller.unmarshal( file );
System.out.println( countres );
}
}
You should separate the mapping of the root element and the nested collection elements.
So you'd have something like this:
#XmlRootElement( name = "Countries" )
public class Countries {
private List<Country> countries;
#XmlElement(name = "Country")
public void setCountries(List<Country> countries) {
this.countries = countries;
}
}
 
#XmlType( propOrder = { "name", "capital", "foundation", "continent" , "population"} )
#XmlRootElement( name = "Country" )
public class Country {
private int population;
private String name;
private String capital;
private int importance;
private String foundation;
private String continent;
#XmlElement(name = "Country_Population")
public void setPopulation(int population) {
this.population = population;
}
#XmlElement(name = "Country_Name")
public void setName(String name) {
this.name = name;
}
#XmlElement(name = "Country_Capital")
public void setCapital(String capital) {
this.capital = capital;
}
#XmlAttribute(name = "importance", required = true)
public void setImportance(int importance) {
this.importance = importance;
}
#XmlElement(name = "Country_foundation")
public void setFoundation(String foundation) {
this.foundation = foundation;
}
#XmlElement(name = "Country_Continent")
public void setContinent(String continent) {
this.continent = continent;
}
}
For more information about dealing with collections in JAXB, see this blog post. #XmlElementWrapper would seem suitable for your task, but I don't think it can be used directly with root elements.

Unmarshal with JAXB

I need to unmarshal a XML string into a class that already exists in my project, but I can't figure out how to unmarshal a certain part of the XML to a list of objects. Let me explain with some code:
I have this XML:
<user>
<id>123</id>
<name>John Doe</name>
<vaddresses>
<address>
<street>Street XYZ</street>
</address>
<address>
<street>Street ABC</street>
</address>
</vaddresses>
</user>
And I have these classes:
User
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "User", propOrder = {"id", "name", "addresses"})
public class User {
#XmlElement
private int id;
#XmlElement
private String name;
#XmlElement
private List<Address> addresses;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
Address
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Address", propOrder = {"street"})
public class Address {
#XmlElement
private String street;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
When I try to unmarshal the XML into these classes, fields id and name are processed correctly, but the same doesn't occur with the addresses:
Output
User:
Id: 123
Nome: John Doe
Addresses: null
How do I solve this problem? Is there some JAXB annotation that I can use? And how? Or do I need to create some type of XmlAdapter? (I've tried this one but without success...)
You can leverage #XmlElementWrapper to add a grouping element around your collection:
#XmlElementWrapper(name="vaddresses")
#XmlElement(name="address")
private List<Address> addresses;
Note
You are adding more annotations than are necessary. JAXB is configuration by exception so you only need to annotate where you want the XML representation to differ from the default.
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html
I think you have to change the type of the private property addresses:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "User", propOrder = {"id", "name", "vaddresses"})
public class User {
#XmlElement
private int id;
#XmlElement
private String name;
#XmlElement
private VAddress vaddresses;
//...
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "VAddress", propOrder = {"addresses"})
public class VAddress {
#XmlElement
private List<Address> addresses;
//...
}
Maybe you can also try the annotation #XmlElementWrapper.
Regards,
User.java
package generated;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"id",
"name",
"vaddresses"
})
#XmlRootElement(name = "user")
public class User {
protected int id;
#XmlElement(required = true)
protected String name;
#XmlElement(required = true)
protected User.Vaddresses vaddresses;
public int getId() {
return id;
}
public void setId(int value) {
this.id = value;
}
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public User.Vaddresses getVaddresses() {
return vaddresses;
}
public void setVaddresses(User.Vaddresses value) {
this.vaddresses = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"address"
})
public static class Vaddresses {
#XmlElement(required = true)
protected List<User.Vaddresses.Address> address;
public List<User.Vaddresses.Address> getAddress() {
if (address == null) {
address = new ArrayList<User.Vaddresses.Address>();
}
return this.address;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"street"
})
public static class Address {
#XmlElement(required = true)
protected String street;
public String getStreet() {
return street;
}
public void setStreet(String value) {
this.street = value;
}
}
}
}
JaxBExample.java
package generated;
import generated.User.Vaddresses.Address;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class JAXBExample {
public static void main(String[] args) {
try {
File file = new File("D:\\StackOverFlow\\JAXBTest\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
User user = (User) jaxbUnmarshaller.unmarshal(file);
System.out.println("ID::"+user.getId());
System.out.println("Name::"+user.getName());
System.out.print("Addresses::");
for(Address address:user.getVaddresses().getAddress())
{
System.out.println(address.getStreet());
}
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
output:--
ID::123
Name::John Doe
Addresses::Street XYZ
Street ABC

JAXB : 2 counts of IllegalAnnotationExceptions

This is my Parser class
public class Test {
public static void main(String args[]) throws Exception {
File file = new File("D:\\Test.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(MyOrder.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
MyOrder customer = (MyOrder) jaxbUnmarshaller.unmarshal(file);
System.out.println(customer.getOrder().getSide());
}
}
This is MyOrder.java file
#XmlRootElement(name = "BXML")
public class MyOrder {
#XmlElement(name = "Bag")
protected Order order;
public MyOrder() {
}
#XmlAttribute
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
This is my Domain Object (Order.java )
#XmlRootElement(name = "BXML")
public class Order {
public Order() {
}
#XmlAttribute(name = "Side")
protected BigInteger Side;
#XmlValue
public BigInteger getSide() {
return Side;
}
public void setSide(BigInteger side) {
Side = side;
}
}
This is the exception that I am getting when I tried to run the program
Exception in thread "main" com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
#XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML.
this problem is related to the following location:
at public com.Order com.MyOrder.getOrder()
at com.MyOrder
Class has two properties of the same name "order"
this problem is related to the following location:
at public com.Order com.MyOrder.getOrder()
at com.MyOrder
this problem is related to the following location:
at protected com.Order com.MyOrder.order
at com.MyOrder
For the #XmlAttribute/#XmlValue need to reference a Java type that maps to text in XML. issue you need to change your initialization of JAXBContext to the following:
JAXBContext jaxbContext = JAXBContext.newInstance(MyOrder.class, Order.class);
For the Class has two properties of the same name "order" issue, you need to change the definition of protected Order order; to private Order order;.
Also, you want to change the #XmlRootElement(name = "BXML") of your Order class to #XmlRootElement(name = "Order").
You can see the below sample code to generate Java Object from given XML.It is working fine in my system.
customer.xml
<?xml version="1.0" encoding="UTF-8"?>
<company>
<customer id="100">
<age>25</age>
<name>Ram</name>
<Address>
<city>Bangalore</city>
<country>India</country>
</Address>
<Address>
<city>Patna</city>
<country>India</country>
</Address>
</customer>
<customer id="200">
<age>26</age>
<name>Ashu</name>
<Address>
<city>Delhi</city>
<country>India</country>
</Address>
<Address>
<city>Madhubani</city>
<country>India</country>
</Address>
</customer>
</company>
Company.java
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="company")
public class Company {
#XmlElement(name="customer")
private List<Costumer> custList;
//
public List<Costumer> getCustList() {
return custList;
}
public void setCustList(List<Costumer> custList) {
this.custList = custList;
}
//
#Override
public String toString() {
return "Company [custList=" + custList + "]";
}
}
Costumer.java
#XmlAccessorType(XmlAccessType.FIELD)
class Costumer {
#XmlElement(name="name")
private String name;
#XmlElement(name="age")
private int age;
#XmlElement(name="id")
private int id;
#XmlElement(name="Address")
private List<Address> addressList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Address> getAddressList() {
return addressList;
}
public void setAddressList(List<Address> addressList) {
this.addressList = addressList;
}
#Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + ", id=" + id + ", addressList=" + addressList + "]";
}
}
Address.java
#XmlAccessorType(XmlAccessType.FIELD)
class Address {
#XmlElement(name="city")
private String city;
#XmlElement(name="country")
private String country;
//
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
//
#Override
public String toString() {
return "Address [city=" + city + ", country=" + country + "]";
}
}
TestMain.java
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class TestMain {
public static void main(String[] args) {
String xmlPath = "C:\\" + File.separator + "customer.xml";
try {
File file = new File(xmlPath);
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] {Company.class,Address.class,Costumer.class});
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Company customer = (Company) jaxbUnmarshaller.unmarshal(file);
System.out.println(customer);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Outout:
Company [custList=[Customer [name=Ram, age=25, id=0, addressList=[Address [city=Bangalore, country=India], Address [city=Patna, country=India]]], Customer [name=Ashu, age=26, id=0, addressList=[Address [city=Delhi, country=India], Address [city=Madhubani, country=India]]]]]
This is because the sub-elements of that class you are creating JAXBcontext instance ,doesn't have the same name as of the element names defined inside it.
Example:
#XmlType(name = "xyz", propOrder = { "a", "b", "c", "d" })
#XmlRootElement(name = "testClass")
public class TestClass
{
#XmlElement(required = true)
protected Status status;
#XmlElement(required = true)
protected String mno;
#XmlElement(required = true)
}
In the above class you don't have "xyz" , but if you will put the property name that is not available JAXBContext instantiation throws IlligalAnnotationException.
If anyone is curious about the usage of JAXB and Lombok.
My fix was to remove the getter and setter from the root object.

Categories

Resources