如何在不创建包装类的情况下编排Jaxb元素列表?

7

除了虚构一个编写器并将每个元素附加到字符串上之外,是否有一种方法可以让JAXB marshaller对对象列表进行编组,我只需给出顶部元素的名称?

我觉得我离成功很近了。

//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);
    }
}

但是结果仍然返回一个空列表。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<pluralName/>

有没有一种方法可以不仅仅是手动粘贴xml字符串就能完成这个操作?

更新

在Michael Glavassevich的帮助下,我已经能够完成这个操作,只有一个限制,即单独的元素是<Item>

//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);
    }
}

结果就变成了:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Basketballs>
    <item>basketball one</item>
    <item>basketball two</item>
</Basketballs>
4个回答

6
如果您不想创建一个包装类,您可以将集合转换为数组,将该数组放置在中,然后进行编组。
例如:
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());
    }
}

产生以下文档的代码如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<items>
    <item>one</item>
    <item>two</item>
</items>

这是朝着正确方向迈出的一步,但我在某个部分遇到了一些麻烦。如果我不知道类型,你知道我该如何将类传递给 JAXBContext.newInstance() 吗?显然我不能使用 JAXBContext.newInstance(T[].class) - mike
其实算了,我已经解决了。不过我还有另一个问题。如果我在你的演示中将Item类名更改为Basketball之类的名称,那么单独的元素仍然会显示为<item>。你有任何想法如何更改它吗? - mike
1
似乎与多维数组的映射规则有关,相当于具有 <xs:element name="item" type=schematype minOccurs="0" maxOccurs="unbounded" nillable="true"/> 的模式类型。至少在规范级别上,这是不可配置的。 - Michael Glavassevich
以上方法需要做哪些更改才能生成JSON而不是XML? - Gaurav Sharma

4
请尝试以下步骤:
首先,创建一个列表类:
@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;
    }
}

然后是Amenity类:

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

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

将所需的设施以列表形式设置在需要的位置 - 可能以较少冗余的方式 :) - 并将其分配给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);

最后,一个toXml方法:
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()
}

获取,即:
<amenityList>
    <amenity>
        <amenityName>a_one</amenityName>
        <amenityDate>today</amenityDate>
    </amenity>
    <amenity>
        <amenityName>a_two</amenityName>
        <amenityDate>tomorrow</amenityDate>
    </amenity>
</amenityList>

我不知道对象列表将是什么,这就是为什么我试图不使用包装类来完成它的原因。 - mike

0

试试这个。

POJO 类:

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

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

马歇尔编码:

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结果:

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

这对我有效。


-1

使用数组的已被接受的解决方案,但会导致每个内部元素都被命名为:<item>

以下解决方案来自于此链接,对我而言效果更好:

是否可能以编程方式配置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();

}

它不需要将列表转换为数组,但需要一个通用类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接