targumon
targumon

Reputation: 1101

annotating the addition of an attribute to an element

I'm upgrading a Java object that currently has XML representation in this spirit:

<myObjects>
    <myObject uid="42" type="someEnum">
        <name>Waldo</name>
        <description>yada yada</description>
        <myElement>some_string</myElement>
        ...
    </myObject>
    ...
</myObjects>

myElement is optional - it can be null in Java / omitted in XML.
I'm adding a field that is only relevant if myElement has an actual value (and to keep compatibility with previous XML, it's optional in itself)

I'm trying to avoid this:

    <myElement>some_string</myElement>
    <myAttr>foo</myAttr>

and prefer something like this:

    <myElement myAttr="foo">some_string</myElement>

but banging my head for 2 days now on how to annotate it.

Upvotes: 2

Views: 400

Answers (2)

targumon
targumon

Reputation: 1101

The answer was dead simple: I'm so used to annotate with XmlElement and XmlAttribute, that I forgot about XmlValue!

The code for MyObject is the same as in Blaise Doughan answer, with one change: myElement is now a class (ElemWithAttr) instead of String:

public class ElemWithAttr {
    @XmlValue
    public String content;

    @XmlAttribute
    public String myAttr;
}

Upvotes: 1

bdoughan
bdoughan

Reputation: 148977

You can use the EclipseLink JAXB (MOXy) @XmlPath extension to easily accomplish this:

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class MyObject {

    @XmlAttribute
    private int uid;

    @XmlAttribute
    private String type;

    private String name;

    private String description;

    private String myElement;

    @XmlPath("myElement/@myAttr")
    private String myAttr;

}

This class will interact with the following XML:

<myObject uid="42" type="someEnum">
    <name>Waldo</name>
    <description>yada yada</description>
    <myElement myAttr="foo">some_string</myElement>
</myObject>

Using the following demo code:

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(MyObject.class);

        File file = new File("input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        MyObject myObject = (MyObject) unmarshaller.unmarshal(file);

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

}

To specify MOXy as your JAXB implementation you need to include a file called jaxb.properties in the same package as your model classes with the following entry:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

For more information on MOXy's XPath based mapping see:

Upvotes: 3

Related Questions