mike
mike

Reputation: 565

How would I marshal a List of Jaxb Elements without making a wrapper class?

Short of actually making up a writer and appending each element onto the string. Is there a way to get the JAXB marshaller to marshall a list of objects where I can just give it the name of the top element?

I feel like I'm close with this

//http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html
public <T> String jaxb(Collection<T> o, Class<T> clazz, String plural){
    try {
        ArrayList<T> al = new ArrayList<T>(o.size());
        al.addAll(o);
        JAXBContext jc = JAXBContext.newInstance(ArrayList.class);
        JAXBElement<ArrayList> amenity = new JAXBElement(new QName(plural), ArrayList.class, al);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        StringWriter writer = new StringWriter();
        marshaller.marshal(amenity, writer);
        return writer.toString();
    } catch (JAXBException e) {
        throw new RuntimeException(e);
    }
}

but the result is still coming back as an empty list

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pluralName/>

Is there a way to do this without just manually pasting strings of xml together?

Update

With some help from Michael Glavassevich I've been able to do this with one caveat, the individual elements are <Item>s

//http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html
@SuppressWarnings({ "unchecked", "rawtypes" })
public <T> String jaxb(Collection<T> elements, Class<T> elementClass, String plural){
    try {
        T[] array = (T[]) Array.newInstance(elementClass, elements.size());
        elements.toArray(array);
        JAXBContext jc = JAXBContext.newInstance(array.getClass());
        JAXBElement<T[]> topElement = new JAXBElement(new QName(plural), array.getClass(), array);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        StringWriter writer = new StringWriter();
        marshaller.marshal(topElement, writer);
        return writer.toString();
    } catch (JAXBException e) {
        throw new RuntimeException(e);
    }
}

The result then becomes

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Basketballs>
    <item>basketball one</item>
    <item>basketball two</item>
</Basketballs>

Upvotes: 7

Views: 21085

Answers (4)

user3851980
user3851980

Reputation: 69

Try this.

POJO class:

@XmlRootElement(name = "classNameTAG")
@XmlAccessorType(XmlAccessType.FIELD)
public class ClassName {

    @XmlElementWrapper(name="listTAG")
    @XmlElement(name="itemTAG") 
    private List<ClassItem> items;
}

The marshall code:

public static void pojo2xml(ClassName classData, File outputFile) throws Exception {
    JAXBContext context;
    BufferedWriter writer = null;
    writer = new BufferedWriter(new FileWriter(outputFile));
    context = JAXBContext.newInstance(ClassName.class);
    Marshaller m = context.createMarshaller();
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    m.marshal(classData, writer);
    writer.close();
}

Xml result:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<classNameTAG>
    <listTAG>
        <itemTAG>data</itemTAG>
        <itemTAG>data</itemTAG>
    </listTAG>
</classNameTAG>

It works for me.

Upvotes: 0

Gonen I
Gonen I

Reputation: 6117

The accepted solution with array works but causes each inner element to be named : < item >

The following solution taken from this link worked better for me:

Is it possible to programmatically configure JAXB?

public class Wrapper<T> {

private List<T> items = new ArrayList<T>();

@XmlAnyElement(lax=true)
public List<T> getItems() {
    return items;
}

}

//JAXBContext is thread safe and so create it in constructor or 
//setter or wherever:
... 
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
... 

public String marshal(List<T> things, Class clazz) {

  //configure JAXB and marshaller     
  Marshaller m = jc.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

  //Create wrapper based on generic list of objects
  Wrapper<T> wrapper = new Wrapper<T>(things);
  JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);

  StringWriter result = new StringWriter();
  //marshal!
  m.marshal(wrapperJAXBElement, result);

  return result.toString();

}

It does not require converting the list to an array, but does require 1 generic general purpose class.

Upvotes: -1

Michael Glavassevich
Michael Glavassevich

Reputation: 1040

If you don't want to create a wrapper class you could convert the collection into an array, place that array in a JAXBElement and then marshal it.

For example:

public class JAXBArrayWriter {

    public static class Item {
        @XmlValue
        protected String value;

        public Item() {}

        public Item(String value) {
            this.value = value;
        }
    }

    public static void main (String [] args) throws Exception {
        List<Item> items = new ArrayList<Item>();
        items.add(new Item("one"));
        items.add(new Item("two"));
        JAXBContext jc = JAXBContext.newInstance(Item[].class);
        JAXBElement<Item[]> root = new JAXBElement<Item[]>(new QName("items"), 
                Item[].class, items.toArray(new Item[items.size()]));
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        StringWriter writer = new StringWriter();
        marshaller.marshal(root, writer);
        System.out.println(writer.toString());
    }
}

which produces the following document:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<items>
    <item>one</item>
    <item>two</item>
</items>

Upvotes: 6

jenjis
jenjis

Reputation: 1097

Please try this:

First, create a list class:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class AmenityList {
    @XmlElement(name = "amenity")
    List<Amenity> amenities = new ArrayList<Amenity>();

    public AmenityList() {}

    public void setList(List<Amenity> amenities) {
        this.amenities = amenities;
    }
}

then the Amenity class:

@XmlAccessorType(XmlAccessType.FIELD)
class Amenity {
    private String amenityName;
    private String amenityDate;

    public Amenity(String name, String date) {
        this.amenityName = name;
        this.amenityDate = date;
    }
}

set where needed your amenities in a list - maybe in a less redundant way :) - and assign it to an AmenityList:

AmenityList amenityList = new AmenityList();
List <Amenity> amenities = new ArrayList<Amenity>();
amenities.add(new Amenity("a_one", "today"));
amenities.add(new Amenity("a_two", "tomorrow"));
amenity.setList(amenities);

and finally, a toXml method:

public static String toXml(AmenityList amenityList) throws JAXBException {
    JAXBContext jaxbContext = JAXBContext.newInstance(AmenityList.class);
    Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
    jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    StringWriter sw = new StringWriter();
    jaxbMarshaller.marshal(amenityList, sw);
    return sw.toString()
}

obtaining, i.e. :

<amenityList>
    <amenity>
        <amenityName>a_one</amenityName>
        <amenityDate>today</amenityDate>
    </amenity>
    <amenity>
        <amenityName>a_two</amenityName>
        <amenityDate>tomorrow</amenityDate>
    </amenity>
</amenityList>

Upvotes: 4

Related Questions