Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I add array indices to the XML produced by JAXB?

Tags:

java

jaxb

I have a Java bean ala

@XmlRootElement public class Bean {
    @XmlElementWrapper(name = "ints") @XmlElement(name = "int")
    int[] values;

    // constructors, getters, setters, etc...
}

JAXB is producing XML like

<bean>
    <ints>
        <int>12</int>
        <int>34</int>
        <int>56</int>
    </ints>
</bean>

I would like the array indices to be included on the <int> tags as array position conveys important value. Preferably as attributes like

<bean>
    <ints>
        <int id='0'>12</int>
        <int id='1'>34</int>
        <int id='2'>56</int>
    </ints>
</bean>

Is there a way to do this?

like image 655
oconnor0 Avatar asked Dec 22 '22 02:12

oconnor0


2 Answers

The order of XML elements does carry information. A sequence of elements has an explicit order, so adding an array index would be redundant, unless you plan on skipping indexes, or having them out-of-order.

Because of this reasoning, JAXB provides no way of doing what you're asking automatically. If you still want to do this, you'll need to wrap your values in a class which incorporates the index values as an @XmlAttribute value:

@XmlRootElement 
public class Bean {
    @XmlElementWrapper(name = "ints")
    @XmlElement(name = "int")
    MyInt[] values;

    // constructors, getters, setters, etc...
}

public class MyInt {
   @XmlAttribute(name="id")
   int id;

   @XmlValue
   int value;
}

It would up to you to make sure the id fields are populated correctly.

like image 194
skaffman Avatar answered Dec 28 '22 10:12

skaffman


You could also do this using an XmlAdapter

Change your Bean class to look like this:

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

@XmlRootElement
public class Bean {

    @XmlElement(name = "ints") 
    @XmlJavaTypeAdapter(IndexAdapter.class)
    int[] values; 

}

The @XmlJavaTypeAdapter allows you to convert a property into another object that JAXB will marshal. This is similar to what was suggested above, without requiring your model to change. The adapter would look like:

import javax.xml.bind.annotation.adapters.XmlAdapter;
import forum.AdaptedArray.AdaptedArrayValue;

public class IndexAdapter extends XmlAdapter<AdaptedArray, int[]>{

    @Override
    public AdaptedArray marshal(int[] v) throws Exception {
        AdaptedArray adaptedArray = new AdaptedArray();
        for(int x=0; x<v.length; x++) {
            adaptedArray.addValue(x, v[x]);
        }
        return adaptedArray;
    }

    @Override
    public int[] unmarshal(AdaptedArray v) throws Exception {
        int[] intArray = new int[v.ints.size()];
        for(AdaptedArrayValue adaptedArrayValue : v.ints) {
            intArray[adaptedArrayValue.index] = adaptedArrayValue.value;
        }
        return intArray;
    }

}

The adapted value class would look like:

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlValue;

public class AdaptedArray {

    @XmlElement(name="int")
    List<AdaptedArrayValue> ints = new ArrayList<AdaptedArrayValue>();;

    public void addValue(int index, int value) {
        AdaptedArrayValue adaptedArrayValue = new AdaptedArrayValue();
        adaptedArrayValue.index = index;
        adaptedArrayValue.value = value;
        ints.add(adaptedArrayValue);
    }

    public static class AdaptedArrayValue {
            @XmlAttribute int index;
            @XmlValue int value;
    }

}
like image 45
bdoughan Avatar answered Dec 28 '22 10:12

bdoughan