请告知序列化JAXB列表的最佳模式

时间:2012-08-13 23:01:02

标签: java json spring jaxb jaxb2

关于为什么列表类型不是序列化的问题有很多问题,但是我质疑以简单的方式提供列表类型bean的好习惯。

到目前为止,我一直在创建内部类以支持包装器,尽管我不喜欢管道,因为我需要为每个pojo执行它。

客户类可能如下所示:

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

    private int id;
    private String name;

    // field accessors

    @XmlRootElement(name = "customers")
    @XmlAccessorType(XmlAccessType.FIELD)
    public static final class CustomerList {
        private List<Customer> customer;
        public CustomerList() {
            this.customer = new ArrayList<>();
        }
        public DataList(List<Customer> list) {
            this.customer = list;
        }
        // customer accessors.
    }

}

我尝试创建像XmlList<T>这样的泛型类并在返回时创建新实例,但JAXB似乎不喜欢这样。

我在Spring / MVC RESTful应用程序中使用它,我需要同时支持JSON和XML。我的JSON应该表示为一个数组,它允许这个方法通过将实现放在JSON调用中然后用XML调用包装来轻松地促进它。

4 个答案:

答案 0 :(得分:1)

我是这样做的。

@XmlRootElement // or @XmlTransient if you want to
public class Plural<S> {

    public static <P extends Plural<S>, S> P newInstance(
            final Class<P> pluralType, final Collection<S> elms) {
        P lt = (P) pluralType.newInstance();
        lt.singulars = new ArrayList<>(elms);
        return lt;
    }

    protected Collection<S> getSingulars() {
        if (singulars == null) {
            singulars = new ArrayList<S>();
        }
        return singulars;
    }

    private Collection<S> singulars;
}

然后你可以制作任何所需的复数类型的任何单数类型。 也许你不喜欢你应该为所有单数类型制作所有复数类,但它可能真的很有帮助,特别是当你想让那些客户开发者看起来更好的时候。

@XmlRootElement
public class Customers extends Plural<Customer> {

    @XmlElement(name = "customer")
    public Collection<Customer> getCustomers() {
        return getSingulars();
    }
}

@XmlRootElement
public class Items extends Plural<Item> {

    @XmlElement(name = "item")
    public Collection<Item> getItems() {
        return getSingulars();
    }
}

@XmlRootElement
public class Invoices extends Plural<Invoice> {

    @XmlElement(name = "invoice")
    public Collection<Invoice> getInvoices() {
        return getSingulars();
    }
}

@XmlRootElement
public class BrettRyans extends Plural<BrettRyan> {

    @XmlElement(name = "brettRyan")
    public Collection<BrettRyan> getBrettRyans() {
        return getSingulars();
    }
}

根据Brett Ryan的评论更新

这里有全功能的源代码。

您可以在http://code.google.com/p/jinahya/source/browse/trunk/com.googlecode.jinahya/stackoverflow/

看到完整的mavenized项目

如果你控制使用字段而不是属性,则JAXB不需要setter。

@XmlTransient
public class Plural<S> {

    public static <P extends Plural<S>, S> P newInstance(
        final Class<P> pluralType) {
        return newInstance(pluralType, Collections.<S>emptyList());
    }

    public static <P extends Plural<S>, S> P newInstance(
        final Class<P> pluralType, final Collection<? extends S> singulars) {
        try {
            final P plural = pluralType.newInstance();
            plural.getSingulars().addAll(singulars);
            return plural;
        } catch (InstantiationException ie) {
            throw new RuntimeException(ie);
        } catch (IllegalAccessException iae) {
            throw new RuntimeException(iae);
        }
    }

    protected Collection<S> getSingulars() {
        if (singulars == null) {
            singulars = new ArrayList<S>();
        }
        return singulars;
    }

    private Collection<S> singulars;
}

@XmlAccessorType(XmlAccessType.NONE)
public class Item {

    public static Item newInstance(final long id, final String name) {
        final Item instance = new Item();
        instance.id = id;
        instance.name = name;
        return instance;
    }

    @Override
    public String toString() {
        return id + "/" + name;
    }

    @XmlAttribute
    private long id;

    @XmlValue
    private String name;
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class Items extends Plural<Item> {


    @XmlElement(name = "item")
    public Collection<Item> getItems() {
        return getSingulars();
    }
}

...测试

public class ItemsTest {

    @Test
    public void testXml() throws JAXBException, IOException {

        final Items marshallable = Plural.newInstance(Items.class);
        for (int i = 0; i < 5; i++) {
            marshallable.getItems().add(Item.newInstance(i, "name" + i));
        }
        for (Item item : marshallable.getItems()) {
            System.out.println("marshallable.item: " + item);
        }

        final JAXBContext context = JAXBContext.newInstance(Items.class);

        final Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

        final ByteArrayOutputStream baos = new ByteArrayOutputStream();

        marshaller.marshal(marshallable, baos);
        baos.flush();

        final Unmarshaller unmarshaller = context.createUnmarshaller();
        final Items unmarshalled = (Items) unmarshaller.unmarshal(
            new ByteArrayInputStream(baos.toByteArray()));
        for (Item item : unmarshalled.getItems()) {
            System.out.println("unmarshalled.item: " + item);
        }
    }
}

打印

marshallable.item: 1/name1
marshallable.item: 2/name2
marshallable.item: 3/name3
marshallable.item: 4/name4
unmarshalled.item: 0/name0
unmarshalled.item: 1/name1
unmarshalled.item: 2/name2
unmarshalled.item: 3/name3
unmarshalled.item: 4/name4

根据Brett Ryan的第二条评论更新

@Test
public void testXsd() throws JAXBException, IOException {

    final JAXBContext context = JAXBContext.newInstance(Items.class);

    context.generateSchema(new SchemaOutputResolver() {
        @Override
        public Result createOutput(final String namespaceUri,
                                   final String suggestedFileName)
            throws IOException {
            return new StreamResult(System.out) {
                @Override
                public String getSystemId() {
                    return "noid";
                }
            };
        }
    });
}

Plural注释时使用@XmlRootElement

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="items" type="items"/>

  <xs:element name="plural" type="plural"/>

  <xs:complexType name="items">
    <xs:complexContent>
      <xs:extension base="plural">
        <xs:sequence>
          <xs:element name="item" type="item" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
      </xs:extension>
    </xs:complexContent>
  </xs:complexType>

  <xs:complexType name="plural">
    <xs:sequence/>
  </xs:complexType>

  <xs:complexType name="item">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="id" type="xs:long" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:schema>

Plural注释时使用@XmlTransient

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="items" type="items"/>

  <xs:complexType name="items">
    <xs:sequence>
      <xs:element name="item" type="item" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="item">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="id" type="xs:long" use="required"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>
</xs:schema>

答案 1 :(得分:0)

对于更多读者。

我刚刚发现了一篇有趣的博客文章,谈论了JAX-RS的自动复数化。 https://blogs.oracle.com/PavelBucek/entry/returning_xml_representation_of_list

我不确定此功能是否是特定于实现的。

每个人都必须尝试平台服务的内容和方式。

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public List<Item> read() {

    final List<Item> items = ... // is the variable name relevant 

    return items;
}

答案 2 :(得分:0)

这可能是我认为的另一种正确方式。

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response readItems() {

    final List<Item> items = ...;

    return Response.ok(new GenericEntity<List<String>>(list) {}).build();
}

答案 3 :(得分:0)

对不起,我发现了另一种我认为更好的方式。

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public List<Item> readItems() {

    final List<Item> items = itemBean.list(...);

    return items;
}

在这种情况下,任何应用程序服务器都可以以不同的方式生成。

使用GlassFish

<items> <!-- smart plural names -->
  <item xmlns="http://www.example.com">
    ...
  </item>
  <item xmlns="http://www.example.com">
    ...
  </item>
<items>

使用JBoss

<collection>
  <item xmlns="http://www.example.com">
    ...
  </item>
  <item xmlns="http://www.example.com">
    ...
  </item>
<collection>

对于客户来说,这个问题不会产生影响。可以像这样使用一般的XPath表达式。

"//item/name/first"