DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Required Capabilities in Self-Navigating Vehicle-Processing Architectures
  • Java: Why a Set Can Contain Duplicate Elements
  • What Is Ant, Really?
  • The Complete Guide to Stream API and Collectors in Java 8

Trending

  • Beyond Simple Responses: Building Truly Conversational LLM Chatbots
  • A Complete Guide to Modern AI Developer Tools
  • Dropwizard vs. Micronaut: Unpacking the Best Framework for Microservices
  • How To Develop a Truly Performant Mobile Application in 2025: A Case for Android
  1. DZone
  2. Coding
  3. Languages
  4. Mapping an Arbitrary List of Objects Using JAXB's @XmlAnyElement and XmlAdapter

Mapping an Arbitrary List of Objects Using JAXB's @XmlAnyElement and XmlAdapter

The @XmlAnyElement annotation enables a property to handle arbitrary XML elements.

By 
Blaise Doughan user avatar
Blaise Doughan
·
Feb. 22, 12 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
32.9K Views

Join the DZone community and get the full member experience.

Join For Free

The @XmlAnyElement annotation enables a property to handle arbitrary XML elements, and the XmlAdapter provides a way to convert an object that can not be mapped into one that can.  In this post we will combine these two mechanisms  to map a list of arbitrary objects.

This post will cover the following concepts:

  1. The @XmlAnyElement annotation
  2. A type level XmlAdapter
  3. Marshalling/Unmarshalling root level simple data types (i.e. String and Integer)
  4. Specifying a root element via JAXBElement
  5. Specifying the type to be unmarshalled on the Unmarshaller

XML


Below is the XML that we will use for this example.  It represents a method call and includes the name of the method and parameter values.  The key detail is that the name of the parameter is represented by the element name, and these element names are not known ahead of time.



<?xml version="1.0" encoding="UTF-8"?>
<method name="addCustomer">
    <id type="java.lang.Integer">123</id>
    <name type="java.lang.String">Jane Doe</name>
    <address type="blog.anyelement.adapted.Address">
        <street>123 A Street</street>
        <city>Any Town</city>
    </address>
</method>

Java Model


Here is the Java model we will use for this post.


Method


This is the root object for our domain model.  We do not know the names and types of all the elements that will correspond to the parameters property, so we will annotate it with @XmlAnyElement.



package blog.anyelement.adapted;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Method {

    private String name;
    private List& lt;Parameter> parameters;

    @XmlAttribute
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlAnyElement
    public List<Parameter> getParameters() {
        return parameters;
    }

    public void setParameters(List<Parameter> parameters) {
        this.parameters = parameters;
    }

}

Parameter


Our parameters have a name and a value.  Since we will need to adapt all instances of Parameter, we will specify a type level XmlAdapter using the @XmlJavaTypeAdapter annotation.

package blog.anyelement.adapted;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlJavaTypeAdapter(ParameterAdapter.class)
public class Parameter {

    private String name;
    private Object value;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

}



Address


This is an example of a domain object that may be set as a parameter value.

package blog.anyelement.adapted;

public class Address {

    private String street;
    private String city;

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

}



ParameterAdapter


In this XmlAdapter we will convert an instance of Parameter to a DOM element that can be handled by the property mapped with @XmlAnyElement.


Unmarshal Operation

  1. Read the type attribute to determine the class for the value object (line 73).
  2. Unmarshal the DOM element using one unmarshal methods that takes a class parameter (line 78).  This parameter tells the JAXB implementation what the target class is.  We need to do this since we have not (and could not have) associated that local root element with a class using @XmlRootElement or @XmlElementDecl.
  3. Build the instance of parameter populated the DOM element (line 82) and JAXBElement (line 83).


Marshal Operation

  1. Build a QName to represent the local root element for the instance of Parameter (line 50).
  2. Create the JAXBElement based on the QName, type of the value, and the value object (line 53).
  3. Marshal the JAXBElement to a DOM element (line 58).
  4. Set the type attribute on the DOM element based on the type of the value object (line 62).


package blog.anyelement.adapted;

import javax.xml.bind.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.namespace.QName;
import javax.xml.parsers.*;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class ParameterAdapter extends XmlAdapter<Element, Parameter> {

    private ClassLoader classLoader;
    private DocumentBuilder documentBuilder;
    private JAXBContext jaxbContext;

    public ParameterAdapter() {
        classLoader = Thread.currentThread().getContextClassLoader();
    }

    public ParameterAdapter(JAXBContext jaxbContext) {
        this();
        this.jaxbContext = jaxbContext;
    }

    private DocumentBuilder getDocumentBuilder() throws Exception {
        // Lazy load the DocumentBuilder as it is not used for unmarshalling.
        if (null == documentBuilder) {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            documentBuilder = dbf.newDocumentBuilder();
        }
        return documentBuilder;
    }

    private JAXBContext getJAXBContext(Class<?> type) throws Exception {
        if (null == jaxbContext) {
            // A JAXBContext was not set, so create a new one based  on the type.
            return JAXBContext.newInstance(type);
        }
        return jaxbContext;
    }

    @Override
    public Element marshal(Parameter parameter) throws Exception {
        if (null == parameter) {
            return null;
        }

        // 1. Build the JAXBElement to wrap the instance of Parameter.
        QName rootElement = new QName(parameter.getName());
        Object value = parameter.getValue();
        Class<?> type = value.getClass();
        JAXBElement jaxbElement = new JAXBElement(rootElement, type, value);

        // 2.  Marshal the JAXBElement to a DOM element.
        Document document = getDocumentBuilder().newDocument();
        Marshaller marshaller = getJAXBContext(type).createMarshaller();
        marshaller.marshal(jaxbElement, document);
        Element element = document.getDocumentElement();

        // 3.  Set the type attribute based on the value's type.
        element.setAttribute("type", type.getName());
        return element;
    }

    @Override
    public Parameter unmarshal(Element element) throws Exception {
        if (null == element) {
            return null;
        }

        // 1. Determine the values type from the type attribute.
        Class<?> type = classLoader.loadClass(element.getAttribute("type"));

        // 2. Unmarshal the element based on the value's type.
        DOMSource source = new DOMSource(element);
        Unmarshaller unmarshaller = getJAXBContext(type).createUnmarshaller();
        JAXBElement jaxbElement = unmarshaller.unmarshal(source, type);

        // 3. Build the instance of Parameter
        Parameter parameter = new Parameter();
        parameter.setName(element.getLocalName());
        parameter.setValue(jaxbElement.getValue());
        return parameter;
    }

}


Demo


The demo code below will load the XML to objects and then marshal the objects back to XML.  We will create an instance of ParameterAdapter based on the JAXBContext (line 11), and set it on both the unmarshaller (line 14) and marshaller (line 19).

package blog.anyelement.adapted;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Method.class, Parameter.class,
                Address.class);
        ParameterAdapter adapter = new ParameterAdapter(jc);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setAdapter(adapter);
        File xml = new File("src/blog/anyelement/adapted/input.xml");
        Method action = (Method) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setAdapter(adapter);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(action, System.out);
    }

}



Download the Source Code


The source code for this post is hosted on GitHub here.  You can download the source as a zip file here.

 

From http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html


Object (computer science) Element

Opinions expressed by DZone contributors are their own.

Related

  • Required Capabilities in Self-Navigating Vehicle-Processing Architectures
  • Java: Why a Set Can Contain Duplicate Elements
  • What Is Ant, Really?
  • The Complete Guide to Stream API and Collectors in Java 8

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: