multi instance xml file ummarshalling to objects - java

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.

Related

I can't read attributes with namespace (ns2) using JAXB to parse XML in Java

I have to read data from this XML file:
<SENT_112 xmlns:ns2="http://www.mf.gov.pl/SENT/2017/01/18/STypes.xsd" xmlns="http://www.mf.gov.pl/SENT/2017/01/18/SENT_112.xsd">
<Carrier><ns2:TraderInfo><ns2:IdSisc>PL957271726800000</ns2:IdSisc><ns2:TraderName>FIRMA SPÓŁKA Z OGRANICZONĄ ODPOWIEDZIALNOŚCIĄ</ns2:TraderName>
<ns2:TraderIdentityType>NIP</ns2:TraderIdentityType>
<ns2:TraderIdentityNumber>9572717268</ns2:TraderIdentityNumber>
</ns2:TraderInfo>
<ns2:TraderAddress><ns2:Street>Arysztacka</ns2:Street><ns2:HouseNumber>91A</ns2:HouseNumber><ns2:City>Cieszyn</ns2:City><ns2:Country>PL</ns2:Country><ns2:PostalCode>43-400</ns2:PostalCode></ns2:TraderAddress></Carrier>
</SENT_112>
My main class is using JAXB:
JAXBContext jc = JAXBContext.newInstance(SENT_112.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
SENT_112 sent_112 = (SENT_112) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(sent_112, System.out);
Carrier.java
public class Carrier implements java.io.Serializable {
private int nrCarrier;
//private String sentnumber;
private String IdSisc;
private String tradername;
private String traderidentitytype;
private String traderidentitynumber;
private String street;
private String housenumber;
private String city;
private String country;
private String postalcode;
//Contrutors
//Getters and Setters
//ToString Method
SENT_112.java
#XmlRootElement(name="SENT_112")
public class SENT_112 {
#XmlElement(name="Carrier")
private List<Carrier> carrier;
public List<Carrier> getCarrier() {
return carrier;
}
PACKAGE-INFO.JAVA
#XmlSchema(
namespace = "http://www.mf.gov.pl/SENT/2017/01/18/SENT_112.xsd",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.przedlak.entity;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
And my Code return that
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SENT_112 xmlns="http://www.mf.gov.pl/SENT/2017/01/18/SENT_112.xsd">
<Carrier>
<nrCarrier>0</nrCarrier>
</Carrier>
</SENT_112>
What is wrong with my Code? I need read all information.
If you want to get all information, write your classes appropriately i.e. to retrieve all data. Change Carrier.java to :
Carrier.java
#XmlRootElement(name="Carrier")
#XmlAccessorType(XmlAccessType.FIELD)
public class Carrier implements java.io.Serializable
{
#XmlElement( name = "TraderInfo" )
private TraderInfo traderInfo;
#XmlElement( name = "TraderAddress" )
private TraderAddress traderAddress;
private int nrCarrier;
public int getNrCarrier() {
return nrCarrier;
}
public void setNrCarrier(int nrCarrier) {
this.nrCarrier = nrCarrier;
}
public TraderInfo getTraderInfo() {
return traderInfo;
}
public void setTraderInfo(TraderInfo traderInfo) {
this.traderInfo = traderInfo;
}
public TraderAddress getTraderAddress() {
return traderAddress;
}
public void setTraderAddress(TraderAddress traderAddress) {
this.traderAddress = traderAddress;
}
}
Then define two classes for TraderAddress, TraderInfo as:
TraderInfo
#XmlRootElement(name="TraderInfo")
#XmlAccessorType(XmlAccessType.FIELD)
public class TraderInfo
{
#XmlElement( name = "IdSisc")
private String IdSisc;
#XmlElement( name = "TraderName")
private String tradername;
#XmlElement( name = "TraderIdentityType")
private String traderidentitytype;
#XmlElement( name = "TraderIdentityNumber")
private String traderidentitynumber;
public String getIdSisc() {
return IdSisc;
}
public void setIdSisc(String idSisc) {
IdSisc = idSisc;
}
public String getTradername() {
return tradername;
}
public void setTradername(String tradername) {
this.tradername = tradername;
}
public String getTraderidentitytype() {
return traderidentitytype;
}
public void setTraderidentitytype(String traderidentitytype) {
this.traderidentitytype = traderidentitytype;
}
public String getTraderidentitynumber() {
return traderidentitynumber;
}
public void setTraderidentitynumber(String traderidentitynumber) {
this.traderidentitynumber = traderidentitynumber;
}
}
TraderAddress.java
#XmlRootElement(name="TraderInfo")
#XmlAccessorType(XmlAccessType.FIELD)
public class TraderAddress
{
#XmlElement( name = "Street")
private String street;
#XmlElement( name = "HouseNumber")
private String houseNumber;
#XmlElement( name = "City")
private String city;
#XmlElement( name = "Country")
private String country;
#XmlElement( name = "PostalCode")
private String postalCode;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getHouseNumber() {
return houseNumber;
}
public void setHouseNumber(String houseNumber) {
this.houseNumber = houseNumber;
}
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;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
}
With this setup, you should get full xml from marshalling:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SENT_112 xmlns="http://www.mf.gov.pl/SENT/2017/01/18/SENT_112.xsd">
<Carrier>
<TraderInfo>
<IdSisc>PL957271726800000</IdSisc>
<TraderName>FIRMA SPÓÅ?KA Z OGRANICZONÄ„ ODPOWIEDZIALNOÅšCIÄ„
</TraderName>
<TraderIdentityType>NIP</TraderIdentityType>
<TraderIdentityNumber>9572717268</TraderIdentityNumber>
</TraderInfo>
<TraderAddress>
<Street>Arysztacka</Street>
<HouseNumber>91A</HouseNumber>
<City>Cieszyn</City>
<Country>PL</Country>
<PostalCode>43-400</PostalCode>
</TraderAddress>
<nrCarrier>0</nrCarrier>
</Carrier>
</SENT_112>

How to convert xml to java

i am trying to convert from xml file to java, i have no experiance with that. So can some one please help me with it.
this is my book class below.
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name= "book")
public class book {
private String ID;
private String title;
private String author;
private String genre;
private String price;
private String publicationDate;
private String discription;
public book(String a, String b, String c, String d, String e, String f, String g ){
ID = a;
title = b;
author = c;
genre = d;
price = e;
publicationDate = f;
discription = g;
}
#XmlElement
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
#XmlElement
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#XmlElement
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
#XmlElement
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
#XmlElement
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
#XmlElement
public String getPublicationDate() {
return publicationDate;
}
public void setPublicationDate(String publicationDate) {
this.publicationDate = publicationDate;
}
#XmlElement
public String getDiscription() {
return discription;
}
public void setDiscription(String discription) {
this.discription = discription;
}
}
now i want to convert the xml file into java objects the xml file i will attach bellow.`
<?xml version="1.0"?>
<catalog>
<book id="1">
<author>Isaac Asimov</author>
<title>Foundation</title>
<genre>Science Ficition</genre>
<price>164</price>
<publish_date>1951-08-21</publish_date>
<description>Excellent.</description>
</book>
<book id="2">
<author>Isaac Asimov</author>
<title>Foundation and Empire</title>
<genre>Science Fiction</genre>
<price>79</price>
<publish_date>1952-10-12</publish_date>
<description>Good.</description>
</book>
</catalog>
so far i tried to convert and read the xml file by creating the method bellow in my man class
enter code here
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void unma() throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(book.class);
Unmarshaller um = jc.createUnmarshaller();
book b = (book) um.unmarshal(new File("src/Dv600/books.xml"));
System.out.println("information");
System.out.println("id" + b.getID());
System.out.println("Author" + b.getAuthor());
System.out.println("description" + b.getDiscription());
}
public static void main(String[] args) throws JAXBException {
unma();
}
}
There are couple of issues with your code.
#XMLAttribute is the correct annotation for id as as per your xml ID
is an attribute of book not an element as author or description.
#XMLElement should be on the setter method not on the getter method
Not sure why you have a constructor in book.java, remove that as well.
What is Catalog, there is no need for that. I have attached the working code please let me know if you have any questions.
************** TEST.java************
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class Test {
public static void unma() throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Books.class);
Unmarshaller um = jc.createUnmarshaller();
Books b = (Books) um.unmarshal(new File("c:/tester/books.xml"));
for (int i =0;i<b.getBooks().size();i++) {
Book bb = b.getBooks().get(i);
System.out.println(bb.getAuthor());
System.out.println(bb.getTitle());
System.out.println(bb.getDescription());
System.out.println(bb.getGenre());
System.out.println(bb.getPrice());
System.out.println(bb.getDate());
}
}
public static void main(String[] args) throws JAXBException {
unma();
}
}
**********Books.java**********
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Books")
public class Books {
List<Book> books;
public List<Book> getBooks() {
return books;
}
#XmlElement( name = "Book" )
public void setBooks( List<Book> books )
{
this.books = books;
}
}
*************Book.java********
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlType( propOrder = { "author", "description","title", "genre","price", "date"} )
#XmlRootElement( name = "Book" )
public class Book
{
String author;
String description;
String title;
String genre;
String price;
String date;
public String getAuthor() {
return author;
}
#XmlElement( name = "author" )
public void setAuthor( String author )
{
this.author = author;
}
public String getDescription() {
return description;
}
#XmlElement( name = "description" )
public void setDescription( String description )
{
this.description = description;
}
public String getTitle() {
return title;
}
#XmlElement( name = "title" )
public void setTitle( String title )
{
this.title = title;
}
public String getGenre() {
return genre;
}
#XmlElement( name = "genre" )
public void setGenre( String genre )
{
this.genre = genre;
}
public String getPrice() {
return price;
}
#XmlElement( name = "price" )
public void setPrice( String price )
{
this.price = price;
}
public String getDate() {
return date;
}
#XmlElement( name = "date" )
public void setDate( String date )
{
this.date = date;
}
}
**xml for book****
<?xml version="1.0"?>
<Books>
<Book id="1">
<author>Isaac Asimov</author>
<title>Foundation</title>
<genre>Science Ficition</genre>
<price>164</price>
<date>1951-08-21</date>
<description>Excellent.</description>
</Book>
<Book id="2">
<author>Isaac Asimov</author>
<title>Foundation and Empire</title>
<genre>Science Fiction</genre>
<price>79</price>
<date>1952-10-12</date>
<description>Good.</description>
</Book>
</Books>

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

#XmlRegistry - how does it work?

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

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