JAXB:如何将Map转换为<key>value</key>格式?

67

这个问题涉及到JAXB Map序列化 - 有很多示例演示了如何将Map序列化为以下结构:

<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),因为我需要动态属性名称 :)

有没有解决方案?

P.S. 目前我不得不为每个想要编组成XML的典型键值对集创建一个专用容器类 - 它可以工作,但我必须创建太多这些辅助容器。


3
类似的问题已经在以下链接中得到回答:https://dev59.com/z1nUa4cB1Zd3GeqPb5Yb#8770027 - Wayne Li
注意:将映射序列化为<key>value</key>是一个不好的想法,因为以数字开头的键会产生格式不正确的XML(xml标签不允许以数字开头)。因此,如果您真的喜欢这样做,必须确保键是有效的XML标签。 - Christian Fries
我在“解组”过程中遇到了类似的问题。我试图将未知元素“解组”为“Map<String,Object>”,但结果并不如预期。我已经在下面提供的链接中发布了完整的问题。如果您有机会,请看一下并提供您的建议:https://stackoverflow.com/questions/67648941/jaxb-moxy-unmarshalling-assigns-all-field-values-to-mapstring-object-rather-th - BATMAN_2008
11个回答

28

你可能有一个合理的原因想要这么做,但一般最好避免生成这种类型的XML。为什么呢?因为这意味着您的地图的XML元素取决于地图的运行时内容。由于XML通常用作外部接口或接口层,这是不可取的。让我解释一下。

Xml模式(xsd)定义了您的XML文档的接口契约。除了能够从XSD生成代码之外,JAXB还可以从代码为您生成XML模式。这使您可以将在XSD中定义的预先协定的结构限制到接口上交换的数据。

对于Map<String, String>的默认情况,生成的XSD将限制地图元素包含多个条目元素,每个元素必须包含一个xs:string键和一个xs:string值。那是一个相当清晰的接口契约。

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

在这种情况下实现严格的契约,您应该使用枚举类型作为地图的键,而不是一个字符串。例如:

public enum KeyType {
 KEY, KEY2;
}

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

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

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

<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转换为MapElement数组,如下所示:
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;
    }
}

有什么方法可以将<item>标签名称更改为自定义的名称,例如<info>? - Tanvir Hossain

27

我发现提供的代码对我不起作用,我找到了另一种映射方式:

MapElements:

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

MapAdapter:

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/


14
我不确定为什么这个投票数那么高,因为它并没有做到被要求的事情。它仍然返回 <key>key</key> <value>value</value>,而不是 <key>value</key>。我基本上是复制并粘贴了这段代码。这是“最佳解决方案”吗? - blo0p3r
3
这不是一个解决已陈述问题的方案。 - NSPKUWCExi2pr8wVoGNk
我同意@blo0p3r的观点,这只是生成了一系列项目: 删除 1 编辑 1 创建 1 - Marcel Piquet
你好,谢谢您的回复。我在我的应用程序中采用了类似的方法,但是遇到了问题,无法获得所需的输出。如果可能的话,您能否请看一下这个问题,在那里我详细解释了我在代码示例中遇到的问题。如果您有一些建议或解决方法,对我来说将非常有帮助: https://stackoverflow.com/questions/67648941/jaxb-moxy-unmarshalling-assigns-all-field-values-to-mapstring-object-rather-th - BATMAN_2008

16

我仍在努力寻找更好的解决方案,但是使用MOXy JAXB,我已经能够处理以下XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <mapProperty>
      <map>
         <key>value</key>
         <key2>value2</key2>
      </map>
   </mapProperty>
</root>
你需要在Map属性上使用@XmlJavaTypeAdapter:
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, String> mapProperty;

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

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

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

}

XmlAdapter的实现如下:

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

import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class MapAdapter extends XmlAdapter<AdaptedMap, Map<String, String>> {

    @Override
    public AdaptedMap marshal(Map<String, String> map) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();
        Element rootElement = document.createElement("map");
        document.appendChild(rootElement);

        for(Entry<String,String> entry : map.entrySet()) {
            Element mapElement = document.createElement(entry.getKey());
            mapElement.setTextContent(entry.getValue());
            rootElement.appendChild(mapElement);
        }

        AdaptedMap adaptedMap = new AdaptedMap();
        adaptedMap.setValue(document);
        return adaptedMap;
    }

    @Override
    public Map<String, String> unmarshal(AdaptedMap adaptedMap) throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        Element rootElement = (Element) adaptedMap.getValue();
        NodeList childNodes = rootElement.getChildNodes();
        for(int x=0,size=childNodes.getLength(); x<size; x++) {
            Node childNode = childNodes.item(x);
            if(childNode.getNodeType() == Node.ELEMENT_NODE) {
                map.put(childNode.getLocalName(), childNode.getTextContent());
            }
        }
        return map;
    }

}

AdaptedMap类是所有魔法的发生地,我们将使用DOM来表示其内容。 通过@XmlAnyElement的组合和类型为Object的属性,我们将欺骗JAXB处理DOM:

import javax.xml.bind.annotation.XmlAnyElement;

public class AdaptedMap {

    private Object value;

    @XmlAnyElement
    public Object getValue() {
        return value;
    }

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

}

这个解决方案需要使用MOXy JAXB实现。您可以通过在模型类中添加名为jaxb.properties的文件并包含以下条目来配置JAXB运行时以使用MOXy实现:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
以下演示代码可用于验证代码:
import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Root root = (Root) unmarshaller.unmarshal(new File("src/forum74/input.xml"));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }
}

如何修改以不包括“map”元素? - Jonah
嗨@Blaise Doughan,有没有更好的解决方案而不需要使用Moxy JAXB? - Nassim MOUALEK

12

我没有看到任何真正解决这个问题的内容。我在这里找到了一个相当好用的东西:

使用JAXB XMLAnyElement类型样式返回动态元素名称

我稍微修改了它以支持哈希树。您可以添加其他集合。

public class MapAdapter extends XmlAdapter<MapWrapper, Map<String, Object>> {
    @Override
    public MapWrapper marshal(Map<String, Object> m) throws Exception {
        MapWrapper wrapper = new MapWrapper();
        List elements = new ArrayList();
        for (Map.Entry<String, Object> property : m.entrySet()) {

            if (property.getValue() instanceof Map)
                elements.add(new JAXBElement<MapWrapper>(new QName(getCleanLabel(property.getKey())),
                        MapWrapper.class, marshal((Map) property.getValue())));
            else
                elements.add(new JAXBElement<String>(new QName(getCleanLabel(property.getKey())),
                        String.class, property.getValue().toString()));
        }
        wrapper.elements = elements;
        return wrapper;
    }

@Override
public Map<String, Object> unmarshal(MapWrapper v) throws Exception {
    HashMap<String, Object> returnval = new HashMap();
    for (Object o : v.elements) {
        Element e = (Element) o;
        if (e.getChildNodes().getLength() > 1) {
            MapWrapper mw = new MapWrapper();
            mw.elements = new ArrayList();
            for (int count = 0; count < e.getChildNodes().getLength(); count++) {
                if (e.getChildNodes().item(count) instanceof Element) {
                    mw.elements.add(e.getChildNodes().item(count));
                }
            }
            returnval.put(e.getTagName(), unmarshal(mw));
        } else {
            returnval.put(e.getTagName(), e.getTextContent());
        }
    }
    return returnval;
}


    // Return a XML-safe attribute.  Might want to add camel case support
    private String getCleanLabel(String attributeLabel) {
        attributeLabel = attributeLabel.replaceAll("[()]", "").replaceAll("[^\\w\\s]", "_").replaceAll(" ", "_");
        return attributeLabel;
    }
}
class MapWrapper {
    @XmlAnyElement
    List elements;
}

然后实现它:

static class myxml {
    String name = "Full Name";
    String address = "1234 Main St";
    // I assign values to the map elsewhere, but it's just a simple
    // hashmap with a hashmap child as an example.
    @XmlJavaTypeAdapter(MapAdapter.class)
    public Map<String, Object> childMap;
}

将这个东西通过简单的Marshaller处理后,输出看起来像这样:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myxml>
    <name>Full Name</name>
    <address>1234 Main St</address>
    <childMap>
        <key2>value2</key2>
        <key1>value1</key1>
        <childTree>
            <childkey1>childvalue1</childkey1>
        </childTree>
    </childMap>
</myxml>

如果您添加了unmarshall函数,那将非常棒。 - Tarik
你真棒!我从昨天开始一直在尝试这个东西,你帮了我很多。非常感谢 :) - BATMAN_2008
@JavaJeff,“marshalling”部分真的帮了我很多。目前我在“unmarshalling”部分卡住了。你能否提供一个“unmarshalling”方法的示例?如果可以的话,那就太好了。 - BATMAN_2008
1
@BATMAN_2008 - 添加了反序列化操作。 - JavaJeff

3
抱歉,无法添加评论。
在Blaise的上面答案中,如果您更改:
@XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, String> getMapProperty() {
    return mapProperty;
}

to:

@XmlJavaTypeAdapter(MapAdapter.class)
@XmlPath(".") // <<-- add this
public Map<String, String> getMapProperty() {
    return mapProperty;
}

那么这样就可以去掉<mapProperty>标签,从而得到:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <map>
        <key>value</key>
        <key2>value2</key2>
    </map>
</root>

或者:

您也可以将其更改为:

@XmlJavaTypeAdapter(MapAdapter.class)
@XmlAnyElement // <<-- add this
public Map<String, String> getMapProperty() {
    return mapProperty;
}

然后你可以完全摆脱AdaptedMap,并直接将MapAdapter更改为编组到Document对象。我只测试了编组功能,因此可能存在反编组问题。

我会尽力找时间制作一个完整的示例,并相应地编辑此帖子。


3

我有一种不需要适配器的解决方案。瞬时映射可以转换为XML元素,反之亦然:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "SchemaBasedProperties")
public class SchemaBasedProperties
{
  @XmlTransient
  Map<String, Map<String, String>> properties;

  @XmlAnyElement(lax = true)
  List<Object> xmlmap;

  public Map<String, Map<String, String>> getProperties()
  {
    if (properties == null)
      properties = new LinkedHashMap<String, Map<String, String>>(); // I want same order

    return properties;
  }

  boolean beforeMarshal(Marshaller m)
  {
    try
    {
      if (properties != null && !properties.isEmpty())
      {
        if (xmlmap == null)
          xmlmap = new ArrayList<Object>();
        else
          xmlmap.clear();

        javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory.newInstance();
        javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();
        org.w3c.dom.Document doc = db.newDocument();
        org.w3c.dom.Element element;

        Map<String, String> attrs;

        for (Map.Entry<String, Map<String, String>> it: properties.entrySet())
        {
          element = doc.createElement(it.getKey());
          attrs = it.getValue();

          if (attrs != null)
            for (Map.Entry<String, String> at: attrs.entrySet())
              element.setAttribute(at.getKey(), at.getValue());

          xmlmap.add(element);
        }
      }
      else
        xmlmap = null;
    }
    catch (Exception e)
    {
      e.printStackTrace();
      return false;
    }

    return true;
  }

  void afterUnmarshal(Unmarshaller u, Object p)
  {
    org.w3c.dom.Node node;
    org.w3c.dom.NamedNodeMap nodeMap;

    String name;
    Map<String, String> attrs;

    getProperties().clear();

    if (xmlmap != null)
      for (Object xmlNode: xmlmap)
        if (xmlNode instanceof org.w3c.dom.Node)
        {
          node = (org.w3c.dom.Node) xmlNode;
          nodeMap = node.getAttributes();

          name = node.getLocalName();
          attrs = new HashMap<String, String>();

          for (int i = 0, l = nodeMap.getLength(); i < l; i++)
          {
            node = nodeMap.item(i);
            attrs.put(node.getNodeName(), node.getNodeValue());
          }

          getProperties().put(name, attrs);
        }

    xmlmap = null;
  }

  public static void main(String[] args)
    throws Exception
  {
    SchemaBasedProperties props = new SchemaBasedProperties();
    Map<String, String> attrs;

    attrs = new HashMap<String, String>();
    attrs.put("ResId", "A_LABEL");
    props.getProperties().put("LABEL", attrs);

    attrs = new HashMap<String, String>();
    attrs.put("ResId", "A_TOOLTIP");
    props.getProperties().put("TOOLTIP", attrs);

    attrs = new HashMap<String, String>();
    attrs.put("Value", "hide");
    props.getProperties().put("DISPLAYHINT", attrs);

    javax.xml.bind.JAXBContext jc = javax.xml.bind.JAXBContext.newInstance(SchemaBasedProperties.class);

    Marshaller marshaller = jc.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.marshal(props, new java.io.File("test.xml"));

    Unmarshaller unmarshaller = jc.createUnmarshaller();
    props = (SchemaBasedProperties) unmarshaller.unmarshal(new java.io.File("test.xml"));

    System.out.println(props.getProperties());
  }
}

我的输出如预期:

<SchemaBasedProperties>
    <LABEL ResId="A_LABEL"/>
    <TOOLTIP ResId="A_TOOLTIP"/>
    <DISPLAYHINT Value="hide"/>
</SchemaBasedProperties>

{LABEL={ResId=A_LABEL}, TOOLTIP={ResId=A_TOOLTIP}, DISPLAYHINT={Value=hide}}

你可以使用元素名称/值对。我需要属性... 玩得开心!

感谢您的回答,帮助我解决了一个困扰我两周的问题。再次感谢。继续分享,编码愉快 :) - BATMAN_2008

2
似乎这个问题有点重复,我已经将一些marshal/unmarshal的解决方案整理在一个帖子中。您可以在此处查看:Dynamic tag names with JAXB
简而言之:
1. 应该创建一个包含@xmlAnyElement的容器类。 2. 可以使用XmlAdapter与@XmlJavaTypeAdapter配对使用,以在容器类和Map<>之间进行转换。

0

使用xml-apis-1.0时,您可以对此进行序列化和反序列化:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <map>
        <key>value</key>
        <key2>value2</key2>
    </map>
</root>

使用这段代码:

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

@XmlRootElement
class Root {

    public XmlRawData map;

}

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Root root = (Root) unmarshaller.unmarshal(new File("src/input.xml"));

        System.out.println(root.map.getAsMap());

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }
}

class XmlRawData {

    @XmlAnyElement
    public List<Element> elements;

    public void setFromMap(Map<String, String> values) {

        Document document;
        try {
            document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        }

        for (Entry<String, String> entry : values.entrySet()) {
            Element mapElement = document.createElement(entry.getKey());
            mapElement.appendChild(document.createTextNode(entry.getValue()));
            elements.add(mapElement);
        }
    }

    public Map<String, String> getAsMap() {
        Map<String, String> map = new HashMap<String, String>();

        for (Element element : elements) {
            if (element.getNodeType() == Node.ELEMENT_NODE) {
                map.put(element.getLocalName(), element.getFirstChild().getNodeValue());
            }
        }

        return map;
    }
}

0
大多数人只提到了这里的marshalling,但实际上这里既有marshalling,也有unmarshalling,还涉及到Map<String,Object>
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.annotation.XmlAnyElement;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.xml.namespace.QName;
import lombok.Getter;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


public class MoxyMapAdapter extends XmlAdapter<MapWrapper, Map<String, Object>> {

  @Override
  public Map<String, Object> unmarshal(MapWrapper value) {
    
    final Map<String, Object> extensions = new HashMap<>();
    //Loop across all elements within value
    for (Object obj : value.getElements()) {
      Element element = (Element) obj;

      final NodeList children = element.getChildNodes();

      if (children.getLength() == 1) {
        extensions.put(element.getNodeName(), element.getTextContent());
      } else {
        List<Object> child = new ArrayList<>();
        for (int i = 0; i < children.getLength(); i++) {
          final Node n = children.item(i);
          if (n.getNodeType() == Node.ELEMENT_NODE) {
            MapWrapper wrapper = new MapWrapper();
            List childElements = new ArrayList();
            childElements.add(n);
            wrapper.elements = childElements;
            child.add(unmarshal(wrapper));
          }
        }
        extensions.put(element.getNodeName(), child);
      }
    }
    return extensions;
  }


  @Override
  public MapWrapper marshal(Map<String, Object> extensions) throws Exception {
    if (extensions == null) {
      return null;
    }

    MapWrapper wrapper = new MapWrapper();

    List elements = new ArrayList();

    //Loop through the Extensions MAP
    for (Map.Entry<String, Object> property : extensions.entrySet()) {

        //If the Value type is MAP then recurse through the loop
        if (property.getValue() instanceof Map) {
          elements
              .add(new JAXBElement<MapWrapper>(new QName(namespaceURI, localPart, prefix), MapWrapper.class, marshal((Map) property.getValue())));
        } else if (property.getValue() instanceof String) {
          // If the Value type is String then directly create JAXBElement
          elements.add(new JAXBElement<String>(new QName(namespaceURI, localPart, prefix), String.class, property.getValue().toString()));
        } else if (property.getValue() instanceof ArrayList) {
          //If the Value type is ArrayList then it contains Duplicate key values so Loop through it
          for (Object dupItems : (ArrayList<String>) property.getValue()) {
            if (dupItems instanceof Map) {
              elements.add(new JAXBElement<MapWrapper>(new QName(namespaceURI, localPart, prefix), MapWrapper.class, marshal((Map) dupItems)));
            } else {
              elements.add(new JAXBElement<String>(new QName(namespaceURI, localPart, prefix), String.class, dupItems.toString()));
            }
          }
        }
    }
    wrapper.elements = elements;
    return wrapper;
  }
}


class MapWrapper {

  @Getter
  @XmlAnyElement
  List elements;
}

0
Jackson有一个XmlMapper,它可以直接支持这个功能,完全不需要编写任何代码。
这里有一个很好的教程:https://www.baeldung.com/jackson-xml-serialization-and-deserialization Maven依赖:
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.9.8</version>
</dependency>

Map 写入 xml:

Map<String, String> map = new HashMap<>();
map.put("SomeKey", "someValue");

XmlMapper mapper = new XmlMapper();
String xml = mapper.writeValueAsString(map);

会给你

<HashMap><SomeKey>someValue</SomeKey></HashMap>

我能够通过创建一个HashMap子类来自定义根元素

@JacksonXmlRootElement(localName = "MyRootElement")
public class XmlHashMap<K, V> extends HashMap<K, V>
{

}

所以现在

Map<String, String> map = new XmlHashMap<>();
map.put("SomeKey", "someValue");

XmlMapper mapper = new XmlMapper();
String xml = mapper.writeValueAsString(map);

会给你

<MyRootElement><SomeKey>someValue</SomeKey></MyRootElement>

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