@XmlRegistry - how does it work?

I found some JAXB2 @XmlRegistry examples over the Internet, but there are no good in-depth tutorials that talk about the concept of using @XmlRegistry with @XmlElementDecl , wondering if this concept is different in general.

Anyway, here is my question: first, some examples of classes that I use to unmount xml with JAXB:

The main class I'm trying to decouple with JAXB is 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); } } } 

Class child - 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); } } } 

xml for 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 cancel the employee XML using the code above, the address list is not populated. The resulting worker object has only an empty address list. Is there something wrong with my comparisons?

To find out what’s going on and see if employee objects are actually created using Object Factory (with @XMLRegistry annotation), I changed the id and name value in Factory methods, but didn’t affect the result, which tells me that JAXB doesn’t actually use ObjectFactory why?

Am I really not mistaken? Any help would be appreciated.

+7
source share
2 answers

@XmlRegistry - how does it work?

@XmlRegistry used to indicate a class with @XmlElementDecl annotations. In order for your JAXB implementation process to use this class, you need to make sure that it is included in the list of classes used to load the JAXBContext . It is not enough to be the static inner class of one of the classes in your domain model:

 JAXBContext context = JAXBContext.newInstance(Employee.class, Employee.XMLObjectFactory.class); 

@XmlElementDecl - how does it work?

If the field / property value is JAXBElement , you need to use @XmlElementDecl . A JAXBElement captures information that may be useful:

  • The name of the element, this is necessary if you map the selection structure, where several elements of the same type. If the element name does not match the unique type, you cannot round 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 a little changed the model of you. I introduced the XmlObjectFactory class, which has two @XmlElementDecl . Both indicate the name address . I used the scope property so that @XmlElementDecl corresponding to the address class is used for the properties in the Employee class.

 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 will cause the property value to be mapped to the name of its root element. Possible matches will include classes mapped to @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 and input.xml from my original answer can be used to run this example.


ORIGINAL RESPONSE

I'm not sure how you are trying to use @XmlRegistry , so I will focus on the following part of your post:

When I format the xml employee using the above code, the address list is not populated. The resulting employee object has only a blank address list. Is there something wrong with my comparisons?

Your list of address objects is wrapped in an address element, so you need to use the @XmlElementWrapper annotation to match this use case. The following 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> 
+16
source

You need to take a List Address object. In this object, you will need to add an object that contains data, such as address bar1. address bar2, etc.

  ie List addrObjList = new List(); addrObjList.add(object); // Bind an object containing data and add one by one 
-one
source

All Articles