Hopefully an easy one for JAXB experts:
I am trying to marshal an immutable class that does not define a default no-arg constructor. I have defined an XmlAdapter implementation but it doesn't seem to be picked up. I have put together a simple self-contained example, which is still failing to work. Can anyone advise what I'm doing wrong?
Immutable Class
#XmlJavaTypeAdapter(FooAdapter.class)
#XmlRootElement
public class Foo {
private final String name;
private final int age;
public Foo(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
}
Adapter and Value Type
public class FooAdapter extends XmlAdapter<AdaptedFoo, Foo> {
public Foo unmarshal(AdaptedFoo af) throws Exception {
return new Foo(af.getName(), af.getAge());
}
public AdaptedFoo marshal(Foo foo) throws Exception {
return new AdaptedFoo(foo);
}
}
class AdaptedFoo {
private String name;
private int age;
public AdaptedFoo() {}
public AdaptedFoo(Foo foo) {
this.name = foo.getName();
this.age = foo.getAge();
}
#XmlAttribute
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#XmlAttribute
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
Marshaller
public class Marshal {
public static void main(String[] args) {
Foo foo = new Foo("Adam", 34);
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Foo.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(foo, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Stack Trace
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Foo does not have a no-arg default constructor.
this problem is related to the following location:
at Foo
at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:451)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:283)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:126)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1142)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:248)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:235)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:445)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:637)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
at Marshal2.main(Marshal2.java:11)
Note that I am using JDK 1.7.0_05.
The following should help:
FOO AS ROOT OBJECT
When #XmlJavaTypeAdapter is specified at the type level it only applies to fields/properties referencing that class, and not when an instance of that class is a root object in your XML tree. This means that you will have to convert Foo to AdaptedFoo yourself, and create the JAXBContext on AdaptedFoo and not Foo.
Marshal
package forum11966714;
import javax.xml.bind.*;
public class Marshal {
public static void main(String[] args) {
Foo foo = new Foo("Adam", 34);
try {
JAXBContext jaxbContext = JAXBContext.newInstance(AdaptedFoo.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(new AdaptedFoo(foo), System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
AdaptedFoo
You will need to add an #XmlRootElement annotation to the AdaptedFoo class. You can remove the same annotation from the Foo class.
package forum11966714;
import javax.xml.bind.annotation.*;
#XmlRootElement
class AdaptedFoo {
private String name;
private int age;
public AdaptedFoo() {
}
public AdaptedFoo(Foo foo) {
this.name = foo.getName();
this.age = foo.getAge();
}
#XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlAttribute
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
FOO AS NESTED OBJECT
When Foo isn't the root object everything works the way you have it mapped. I have extended your model to demonstrate how this would work.
Bar
package forum11966714;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Bar {
private Foo foo;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
}
Demo
Note that the JAXB reference implementation will not let you specify the Foo class when bootstrapping the JAXBContext.
package forum11966714;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Bar.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
File xml = new File("src/forum11966714/input.xml");
Bar bar = (Bar) jaxbUnmarshaller.unmarshal(xml);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(bar, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<bar>
<foo name="Jane Doe" age="35"/>
</bar>
I know that this is not the case, but if you have such an error when you put #XmlJavaTypeAdapter for the field - check that you specified the namespace. You may need it.
In my case this didn't work:
#XmlElement(name = "Expiration")
#XmlJavaTypeAdapter(DateAdapter.class)
private Date expiration;
until a namespace has been specified:
#XmlElement(name = "Expiration", namespace="http://site/your.namespace")
#XmlJavaTypeAdapter(DateAdapter.class)
private Date expiration;
Related
I am having an issue with my unmarshaller. I have a file that looks like the following:
<Employee xmlns="namespace here">
<Employee>
<Id>2</Id>
<Name>idk</Name>
</Employee>
</Employee>
The problem is the root element and the list of elements are the same name "Employee". When I go to unmarshal I get a classcastexception.
#XmlRootElement(name="Employee")
public class EmployeeInformation {
List<EmployeeInformationElement> elements;
private String errorCode;
private String errorMessage;
public List<EmployeeInformationElement> getElements() {
return elements;
}
#XmlElement(name="Employee")
public void setElements(List<EmployeeInformationElement> elements) {
this.elements = elements;
}
public String getErrorCode() {
return errorCode;
}
#XmlElement(name="ErrorCode")
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
#XmlElement(name="ErrorMessage")
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
I am able to use this code to marshal a file that looks exactly like the file I need to unmarshal. So I am confused. What is missing so when I unmarshal, the unmarshaller does not give me the following exception:
java.lang.ClassCastException: XXXX.EmployeeInformationElement cannot be cast to XXXX.EmployeeInformation
Unable to reproduce (tested on Java 1.8.0_65).
Since you didn't provide an MCVE (Minimal, Complete, and Verifiable example), here is one that works.
Only known difference is that namespace was removed for simple testing.
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
public class Test {
public static void main(String[] args) throws Exception {
String xml = "<Employee>\r\n" +
"<Employee>\r\n" +
" <Id>2</Id>\r\n" +
" <Name>idk</Name>\r\n" +
"</Employee>\r\n" +
"</Employee>\r\n";
JAXBContext jaxbContext = JAXBContext.newInstance(EmployeeInformation.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
EmployeeInformation empInfo = (EmployeeInformation)unmarshaller.unmarshal(new StringReader(xml));
System.out.println(empInfo);
}
}
#XmlRootElement(name="Employee")
class EmployeeInformation {
private List<EmployeeInformationElement> elements;
#XmlElement(name="Employee")
public List<EmployeeInformationElement> getElements() {
return elements;
}
public void setElements(List<EmployeeInformationElement> elements) {
this.elements = elements;
}
}
class EmployeeInformationElement {
private int id;
private String name;
#XmlElement(name="Id")
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
#XmlElement(name="Name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
When I try to unmarshal my Person.xml to a POJO and print it out the parent object prints out fine but ChildThree.java prints out null?
Test.java
public class Test {
public static void main(String[] args) {
try {
File file = new File("src/xml/person.xml");
System.out.println(file.getAbsolutePath());
JAXBContext jaxbContext = JAXBContext.newInstance(Parent.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Parent parent = (Parent) jaxbUnmarshaller.unmarshal(file);
System.out.println(parent);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
Person.xml
<?xml version="1.0" encoding="utf-8"?>
<Parent>
<ChildOne>1</Child>
<ChildTwo>2</Child>
<ChildThree>
<Name>3</Name>
<Age>Ten</Age>
</ChildThree>
</Parent>
Parent.java
#XmlRootElement(name = "Parent")
#XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
#XmlElement
private String ChildOne;
#XmlElement
private String ChildTwo;
#XmlElement
private ChildThree ChildThree;
public String getChildOne() {
return ChildOne;
}
public void setChildOne() {
this.ChildOne = ChildOne;
}
public String getChildTwo() {
return ChildTwo;
}
public void setChildTwo() {
this.ChildTwo = ChildTwo;
}
public ChildThree getChildThree() {
return ChildThree;
}
public void setChildThree() {
this.ChildThree = ChildThree;
}
}
ChildThree.java
#XmlRootElement(name = "ChildThree")
#XmlAccessorType(XmlAccessType.FIELD)
public class ChildThree {
#XmlElement
private String Name;
#XmlElement
private String Age;
public String getName() {
return Name;
}
public void setName() {
this.Name = Name;
}
public String getAge() {
return Age;
}
public void setAge() {
this.Age = Age;
}
}
The following should help:
When you specify #XmlAccessorType(XmlAccessType.FIELD) on your class you should put your annotations on the field (instance variable) and not the property (get/set method), (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).
Make sure your get/set methods follow the appropriate bean conventions:
String getFoo()
void setFoo(String)
Recognize that JAXB has rules for converting field/property names in Java to XML names that may differ from your expectations. When you have unmarshalling problems it is often useful to populate the object model and marshal it to see what JAXB is expecting (see: http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html).
Fixed it by replacing:
#XmlElement
private ChildThree ChildThree;
with:
#XmlElement(name = "ChildThree")
private List<ChildThree> childThree;
I am having trouble unmarshalling a simple list of elements with JAXB. I have simplified my model further and still have a problem.
I have 2 element classes, a Classroom element and a Student element. I have modelled them in Java like below:
#XmlRootElement(name = "classroom", namespace = "http://www.info.com/school/model")
#XmlAccessorType(XmlAccessType.FIELD)
public class Classroom {
#XmlElementWrapper(name = "students")
#XmlElement(name = "student", type = Student.class)
private List<Student> students;
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
private String name;
private String gender;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
When I try to unmarshal the classroom element, it is unable to unmarshal the list of students. For example the following code should print out to System out "Number of Pupils is: 2", yet when I run it I get "Number of Pupils is: 0".
Is someone able to point me in the right direction for configuring the JAXB annotations so that I can unmarshal this?
I have even added a toString methof call on the JAXB context and I can see that the Student class is listed as well.
public static void main(String[] args) {
SimpleJaxbTestForLists sjtfl = new SimpleJaxbTestForLists();
sjtfl.unmarshal("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><classroom xmlns=\"http://www.info.com/school/model\"><students><student><name>Test Student 1</name><gender>Male</gender><age>12</age></student><student><name>Test Student 2</name><gender>Female</gender><age>12</age></student></students></classroom>");
}
public void unmarshal(String xmlContent) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Classroom.class);
System.out.println(jaxbContext.toString());
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlContent);
Classroom classroom = (Classroom) jaxbUnmarshaller.unmarshal(reader);
System.out.println("Number of Pupils is: " + classroom.getStudents().size());
} catch (JAXBException ex) {
ex.printStackTrace();
}
}
Since your XML document specifies a default namespace, you can leverage a package level #XmlSchema annotation to map the namespace qualification:
package-info.java
#XmlSchema(
namespace = "http://www.info.com/school/model",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
You can read more about JAXB and namespace qualification on my blog:
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
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
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.