创建一个JAX-RS对象,其中包含集合和基元作为地图的值

时间:2013-07-19 19:06:24

标签: xml jaxb jackson moxy

我尝试创建一个JAX-RS服务,允许将基元,地图和列表的混合值作为地图和列表的值。域模型中的所有属性都无法明确定义,因为数据将来自不同的系统,其中某些属性将存在,​​而其他属性则不存在。该服务需要支持XML和JSON。 JSON结构需要与下面列出的类似,不需要因包装类而额外嵌套元素。 XML结构尚未设置,更具有可塑性。

到目前为止,我一直在使用Resteasy和Jackson,并且一直在回避供应商特定的注释,但我意识到这可能是不可避免的。这些服务API未锁定,我对其他可以实现此目标的建议持开放态度。一个要求是这是一个标准的REST服务。

表示文章的示例对象:

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

    @XmlElement
    private String title;

    @XmlElement
    private String creator;

    @XmlElement
    private Map<String, Object> attributes;

    public Map<String, Object> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, Object> attributes) {
        this.attributes = attributes;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

鉴于此对象,我希望JSON看起来像:

{
    "title": "Some Title",
    "attributes":
    {
        "relevance": 0.93,
        "rating": 4,
    },
    "actions":
    [
        "A",
        "C"
    ]
}

和XML看起来像:

<article>
    <title>Some Title</title>
    <attributes>
        <entry>
            <key>relevance</key>
            <value xsi:type="xs:double">0.93</value>
        </entry>
        <entry>
            <key>rating</key>
            <value xsi:type="xs:int">4</value>
        </entry>
        <entry>
            <key>actions</key>
            <value>
                <list xsi:type="xs:string">A</list>
                <list xsi:type="xs:string">C</list>
          </value>
        </entry>
    </attributes>
</article>

我遇到的问题是,如果map的值是标准的ArrayList,则JSON处理工作正常,但XML for不包含有关数组内容的信息。 XML如下所示:

<article>
    <title>Some Title</title>
    <attributes>
        <entry>
            <key>relevance</key>
            <value xsi:type="xs:double">0.93</value>
        </entry>
        <entry>
            <key>rating</key>
            <value xsi:type="xs:int">4</value>
        </entry>
        <entry>
            <key>actions</key>
            <value>
   ----->     <arraylist/>
          </value>
        </entry>
    </attributes>
</article>

要在列表的值需要将列表包含在具有JAXB注释的对象中时显示列表的内容。但是,这样做会在访问属性时为JSON添加另一个级别,这是不希望的。如果使用XmlAdaptor解包嵌套对象,则XML会再次返回该值。

理想情况下,我希望对地图和列表进行强类型化,以便只添加对其具有JAXB注释的对象包装器。

类似下面的层次结构:

根类

@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso({JaxbList.class, JaxbMap.class, JaxbString.class})
abstract public class JaxbObject<E> {

    @XmlElement
    abstract public E getValue();
    abstract public void setValue(E value);
}

实施课程

@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso({JaxbObject.class})
public class JaxbList extends JaxbObject<ArrayList<JaxbObject<?>>> {

    private ArrayList<JaxbObject<?>> value;

    public ArrayList<JaxbObject<?>> getValue() {
        return this.value;
    }

    public void setValue(ArrayList<JaxbObject<?>> value) {
        this.value = value;
    }
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso({JaxbObject.class})
public class JaxbMap extends JaxbObject<HashMap<String, JaxbObject<?>>> {

    private HashMap<String, JaxbObject<?>> value;

    public HashMap<String, JaxbObject<?>> getValue() {
        return this.value;
    }

    public void setValue(HashMap<String, JaxbObject<?>> value) {
        this.value = value;
    }
}


@XmlAccessorType(XmlAccessType.NONE)
@XmlType
final public class JaxbString extends JaxbObject<String> {

    private String value;

    public String getValue() {
        return this.value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}


@XmlAccessorType(XmlAccessType.NONE)
@XmlType
final public class JaxbInt extends JaxbObject<Integer> {

    private Integer value;

    public Integer getValue() {
        return this.value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }
}

我希望如果可以使用@XmlValue注释getValue(),那么包装类将不会添加到XML或JSON中,但@XMLValue在该位置无效。我确实看到有关@XMLValue的帖子在Moxy的该位置有效,但是要转移到Moxy,我需要知道我试图做的事情是由它支持的。

1 个答案:

答案 0 :(得分:1)

一些想法:

  1. Jackson可以使用JAXB注释以及它自己的注释:默认情况下,您的JAX-RS容器可能支持也可能不支持,但即使不支持,启用也很容易
  2. Jackson也可以使用其https://github.com/FasterXML/jackson-dataformat-xml的XML模块输出XML - 可以使用Jackson和/或JAXB注释
  3. 关于XML,需要注意的一点是,使用JSON,您通常可以获得任何合理的JSON来映射到Java对象,XML有自己的特性,并且可能更难以获得 exact 两侧结构。因此,如果可能的话,最好关注您传递的数据,并找出好的Object表示,然后看看可以生成哪种XML表示形式。我意识到,在处理其他人生成的XML时,这可能并不总是一种选择。但是能够接受轻微的差异会使生活变得更加容易,尤其是在处理Java ListMap时。

    关于JAXB注释:请记住它们是特定于XML的,因此您可以平衡对特定于供应商的注释的担忧,并考虑在JSON(和/或其他数据类型)上使用XML注释。避免或最小化注释使用的能力是一种好方法;通过使用标准命名约定和默认结构;也就是说,试图避免使用数据格式表示过多修补,并接受默认映射。