Marshall / Unmarshall使用JaxB嵌套地图

时间:2016-02-08 17:05:50

标签: java groovy jaxb

以前有几个关于使用JaxB来编组/解组java.util.Map的问题,其中许多问题都指向了这个例子,这很好用:

http://blog.bdoughan.com/2013/03/jaxb-and-javautilmap.html

但是,如果地图不是@XmlRootElement的成员,我无法让JaxB能够编组/解组地图实例。例如,这是一个根元素类,

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

    private MyField myField

    MyField getMyField() {
        return myField
    }

    void setMyField(MyField myField) {
        this.myField = myField
    }

}

它的字段类的定义:

@XmlAccessorType(XmlAccessType.FIELD)
public static class MyField{

    Map<String, String> getSomeMap() {
        return someMap
    }

    void setSomeMap(Map<String, String> someMap) {
        this.someMap = someMap
    }

    @XmlElement
    private Map<String, String> someMap = new HashMap<String, String>()
}

还有一些驱动编组的代码:

    JAXBContext jc = JAXBContext.newInstance(Customer.class)

    Customer customer = new Customer()
    MyField myField1 = new MyField()
    myField1.someMap.put("foo", "bar")
    myField1.someMap.put("baz", "qux")
    customer.myField =  myField1

    Marshaller marshaller = jc.createMarshaller()
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true)
    marshaller.marshal(customer, System.out)

此示例导致:

java.util.Map is an interface, and JAXB can't handle interfaces.
java.util.Map does not have a no-arg default constructor.

我在Groovy而不是Java中编写代码,但我认为它不会产生太大的影响。

3 个答案:

答案 0 :(得分:1)

通过使用Spring Boot创建一个@RestController类型的TestController,我能够使用JAXB遇到相同的行为。

import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping(value = "test")
class TestController {

    @RequestMapping(value = "findList")
    List findList() {
        ["Test1", "Test2", "Test3"] as ArrayList<String>
    }

    @RequestMapping(value = "findMap")
    Map findMap() {
        ["T1":"Test1", "T2":"Test2", "T3":"Test3"] as HashMap<String,String>
    }

    @RequestMapping(value = "")
    String find(){
        "Test Something"
    }
}

使用JAXB作为SpringBoot中的默认实现,我可以重现/ test / findList将正确呈现XML的问题,但/ test / findMap会产生错误,如初始发布中所述。

对我来说,问题的解决方案是将XML渲染库切换到Jackson(还有其他像XStream)。

使用Gradle作为构建文件(build.gradle),我只需添加Jackson依赖项,非常类似于使用Maven时的情况:

  

&#39; com.fasterxml.jackson.core:杰克逊 - 芯:2.7.1&#39 ;,     &#39; com.fasterxml.jackson.core:杰克逊 - 注解:2.7.1&#39 ;,     &#39; com.fasterxml.jackson.core:杰克逊 - 数据绑定:2.7.1-1&#39 ;,     &#39; com.fasterxml.jackson.dataformat:杰克逊 - DATAFORMAT-XML:2.7.1&#39 ;,     &#39; org.codehaus.woodstox:woodstox芯-ASL:4.4.1&#39;,

答案 1 :(得分:0)

我在自己面前经历过这个。底线是警告告诉你确切的问题。您已将字段定义为java.util.Map类型。 JAXB不支持接口。要纠正您的问题,您需要将字段的声明更改为具体的Map类型,如:

private HashMap<String, String> someMap = new HashMap<String, String>()

您引用的链接中描述了您的其他选项。你需要一个

MapAdapter 类在您提供的链接中引用,然后将其包含在注释中,向JAXB提示如何编组/解组Map类型。

我认为这个链接提供了一个更清晰的示例,说明如何创建和实现MapAdapter:

JAXB: how to marshall map into <key>value</key>

答案 2 :(得分:0)

我遇到的具体问题的答案最终是从@XmlElement字段中移除Map注释,如下所示:

@XmlAccessorType(XmlAccessType.FIELD)
public static class MyField{

  Map<String, String> getSomeMap() {
      return someMap
  }

  void setSomeMap(Map<String, String> someMap) {
      this.someMap = someMap
  }

  //@XmlElement Remove this annotation
  private Map<String, String> someMap = new HashMap<String, String>()
}  

如果没有该注释,编组/解组工作正常,并仍然将Map解释为XmlElement - 具体来说,该注释似乎存在错误。但是,正如@dlcole指出的那样,另一种选择(允许您对序列化表示的格式有更多控制权)是使用Jackson而不是JAXB。