JAXB:如何将映射编组到<键>值</键>

2022-08-31 14:47:49

问题是关于 JAXB Map 编组的 - 有很多关于如何将 Map marhsall 成结构的示例,如下所示:

<map>
  <entry>
    <key> KEY </key>
    <value> VALUE </value>
  </entry>
  <entry>
    <key> KEY2 </key>
    <value> VALUE2 </value>
  </entry>
  <entry>
  ...
</map>

事实上,JAXB 本身就支持这一点。但是,我需要的是 XML,其中键是元素名称,值是其内容:

<map>
  <key> VALUE </key>
  <key2> VALUE2 </key2>
 ...
</map>

我没有成功地按照JAXB开发人员推荐的方式实现我的Map适配器(https://jaxb.dev.java.net/guide/Mapping_your_favorite_class.html),因为我需要,他 - 动态属性名称:)

有什么解决方案吗?

附言:目前,我必须为我想要编组到XML的每组典型的键值对创建一个专用的容器类 - 它可以工作,但我必须创建太多的这些帮助器容器。


答案 1

您可能希望这样做可能有一个有效的理由,但通常最好避免生成这种 XML。为什么?因为这意味着地图的 XML 元素依赖于地图的运行时内容。由于XML通常用作外部接口或接口层,因此这是不可取的。让我解释一下。

Xml 架构 (xsd) 定义 XML 文档的接口协定。除了能够从 XSD 生成代码之外,JAXB 还可以从代码中为您生成 XML 模式。这允许您将通过接换的数据限制为 XSD 中定义的预先商定的结构。

在 a 的默认情况下,生成的 XSD 将限制映射元素包含多个条目元素,每个元素必须包含一个键和一个值。这是一个非常清晰的界面契约。Map<String, String>xs:stringxs:string

您描述的是,您希望 xml 映射包含其名称将在运行时由映射内容确定的元素。然后,生成的 XSD 只能指定映射必须包含编译时类型未知的元素列表。在定义接口协定时,通常应避免这种情况。

在这种情况下,若要实现严格的协定,应使用枚举类型作为映射的键,而不是 String。例如:

public enum KeyType {
 KEY, KEY2;
}

@XmlJavaTypeAdapter(MapAdapter.class)
Map<KeyType , String> mapProperty;

这样,您希望成为 XML 中元素的键在编译时是已知的,因此 JAXB 应该能够生成一个模式,该模式将使用预定义的键 KEY 或 KEY2 之一将 map 的元素限制为元素。

另一方面,如果您希望简化默认生成的结构

<map>
    <entry>
        <key>KEY</key>
        <value>VALUE</value>
    </entry>
    <entry>
        <key>KEY2</key>
        <value>VALUE2</value>
    </entry>
</map>

对于像这样更简单的东西

<map>
    <item key="KEY" value="VALUE"/>
    <item key="KEY2" value="VALUE2"/>
</map>

您可以使用 MapAdapter 将 Map 转换为 MapElements 数组,如下所示:

class MapElements {
    @XmlAttribute
    public String key;
    @XmlAttribute
    public String value;

    private MapElements() {
    } //Required by JAXB

    public MapElements(String key, String value) {
        this.key = key;
        this.value = value;
    }
}


public class MapAdapter extends XmlAdapter<MapElements[], Map<String, String>> {
    public MapAdapter() {
    }

    public MapElements[] marshal(Map<String, String> arg0) throws Exception {
        MapElements[] mapElements = new MapElements[arg0.size()];
        int i = 0;
        for (Map.Entry<String, String> entry : arg0.entrySet())
            mapElements[i++] = new MapElements(entry.getKey(), entry.getValue());

        return mapElements;
    }

    public Map<String, String> unmarshal(MapElements[] arg0) throws Exception {
        Map<String, String> r = new TreeMap<String, String>();
        for (MapElements mapelement : arg0)
            r.put(mapelement.key, mapelement.value);
        return r;
    }
}

答案 2

提供的代码对我不起作用。我找到了另一种地图方式:

地图元素 :

package com.cellfish.mediadb.rest.lucene;

import javax.xml.bind.annotation.XmlElement;

class MapElements
{
  @XmlElement public String  key;
  @XmlElement public Integer value;

  private MapElements() {} //Required by JAXB

  public MapElements(String key, Integer value)
  {
    this.key   = key;
    this.value = value;
  }
}

地图适配器 :

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.adapters.XmlAdapter;

class MapAdapter extends XmlAdapter<MapElements[], Map<String, Integer>> {
    public MapElements[] marshal(Map<String, Integer> arg0) throws Exception {
        MapElements[] mapElements = new MapElements[arg0.size()];
        int i = 0;
        for (Map.Entry<String, Integer> entry : arg0.entrySet())
            mapElements[i++] = new MapElements(entry.getKey(), entry.getValue());

        return mapElements;
    }

    public Map<String, Integer> unmarshal(MapElements[] arg0) throws Exception {
        Map<String, Integer> r = new HashMap<String, Integer>();
        for (MapElements mapelement : arg0)
            r.put(mapelement.key, mapelement.value);
        return r;
    }
}

根元素 :

import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
public class Root {

    private Map<String, Integer> mapProperty;

    public Root() {
        mapProperty = new HashMap<String, Integer>();
    }

    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map<String, Integer> getMapProperty() {
        return mapProperty;
    }

    public void setMapProperty(Map<String, Integer> map) {
        this.mapProperty = map;
    }

}

我在这个网站上找到了代码:http://www.developpez.net/forums/d972324/java/general-java/xml/hashmap-jaxb/


推荐