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
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
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

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Required Capabilities in Self-Navigating Vehicle-Processing Architectures
  • Java: Why a Set Can Contain Duplicate Elements
  • What Is Ant, Really?
  • Top Java Collection Interview Questions for 2021

Trending

  • A Complete Guide to Open-Source LLMs
  • Development of Custom Web Applications Within SAP Business Technology Platform
  • Adopting Agile Practices for Workforce Management: Benefits, Challenges, and Practices
  • Edge Data Platforms, Real-Time Services, and Modern Data Trends
  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.

Blaise Doughan user avatar by
Blaise Doughan
·
Feb. 22, 12 · Tutorial
Like (0)
Save
Tweet
Share
30.85K 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?
  • Top Java Collection Interview Questions for 2021

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • 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: