java - JAXB 解码 XML 元素以映射

标签 java xml jaxb

我有一个 XML 文件。在此文件中,某些元素的属性会发生变化。我想将这些属性放入 map 中。我该怎么做?

我的 XML 是:

<ROW id='1'>
    <MOBILE>9831138683</MOBILE>
    <VARS>
        <CAUSE>Delayed payment</CAUSE>
        <DO>100.56</DO>
        <LOT>1</LOT>
    </VARS>
</ROW>
<ROW id='2'>
    <MOBILE>9831138684</MOBILE>
    <VARS>
        <NAME>hi</NAME>
        <ADDRESS>Here</ADDRESS>
        <LOT>2</LOT>
    </VARS>
</ROW>

在此,VARS 元素可以具有变化的属性,我事先不知道这些元素是什么。

我为此创建了一个类:

@XmlRootElement(name = "ROW")
@XmlAccessorType(XmlAccessType.FIELD)
public class SMSDetail {
    @XmlAttribute
    private int id;
    @XmlElement(name = "MOBILE")
    private int mobileNo;
    @XmlElement(name = "VARS")
    @XmlJavaTypeAdapter(MapAdapter.class)
    private HashMap<String, String> variableMap;

    public int getId() {
        return id;
    }

    public int getMobileNo() {
        return mobileNo;
    }

    public HashMap<String, String> getVariableMap() {
        return variableMap;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setMobileNo(int mobileNo) {
        this.mobileNo = mobileNo;
    }

    public void setVariableMap(HashMap<String, String> variableMap) {
        this.variableMap = variableMap;
    }
}

我想将 VARS 元素映射到 Map。我希望将 CAUSELOT 等标签作为键,将它们的值作为 map 中的值。为此,我编写了一个 XmlAdapater:

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

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

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

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

此适配器为我提供了 variableMap 变量的 null。为此,Adapter 应该如何修改?

最佳答案

您可以执行以下操作:

XmlAdapter(MapAdapter)

您可以为您的 XmlAdapter 执行以下操作,将 Map 的实例转换为具有 DOM List 的对象>元素。您将构造 Element 的实例,以便名称是它们来自映射条目的键,文本内容是值。

import java.util.*;
import java.util.Map.Entry;

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

import org.w3c.dom.*;

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

    private DocumentBuilder documentBuilder;

    public MapAdapter() throws Exception {
        documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    }

    public static class AdaptedMap {        
        @XmlAnyElement
        public List<Element> elements = new ArrayList<Element>();
    }

    @Override
    public AdaptedMap marshal(Map<String, String> map) throws Exception {
        Document document = documentBuilder.newDocument();
        AdaptedMap adaptedMap = new AdaptedMap();
        for(Entry<String, String> entry : map.entrySet()) {
            Element element = document.createElement(entry.getKey());
            element.setTextContent(entry.getValue());
            adaptedMap.elements.add(element);
        }
        return adaptedMap;
    }

    @Override
    public Map<String, String> unmarshal(AdaptedMap adaptedMap) throws Exception {
        HashMap<String, String> map = new HashMap<String, String>();
        for(Element element : adaptedMap.elements) {
            map.put(element.getLocalName(), element.getTextContent());
        }
        return map;
    }


}

优化MapAdapter的使用

为了提高性能,我们希望尽量减少 DocumentBuiderFactoryDocumentBuilder 的实例化次数。为此,我们可以创建供 JAXB 使用的 MapAdapter 实例,并将其设置在 MarshallerUnmarshaller 上。这样 JAXB 将使用该实例,而不是每次需要适配器时都创建一个新实例。

import java.io.File;
import javax.xml.bind.*;

public class Demo {

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

        MapAdapter mapAdapter = new MapAdapter();

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setAdapter(mapAdapter);
        File xml = new File("src/forum27182975/input.xml");
        SMSDetail smsDetail = (SMSDetail) unmarshaller.unmarshal(xml);

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

}

如果您使用 MOXy 作为 JAXB (JSR-222) 提供者

如果您使用 MOXy 作为 JAXB 提供者,那么您可以利用 @XmlVariableNode 扩展来简化此用例的映射:

关于java - JAXB 解码 XML 元素以映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27182975/

相关文章:

java - 使用表达语言进行转换

java - 使用 org.springframework.jdbc.datasource.init.ScriptUtils 在 unix 中执行一个 .sql 文件

xml - XPath:仅基于 text() 获取以下 sibling

c# - LINQ:根据属性值从 XML 中删除元素?

java - JAXB 编码,忽略 nillable

java - 如何使用 JAXB 解码包含混合标签(具有属性,并且具有嵌套标签的内容值)的 XML 文件?

java - 在不使用 pro-guard 的情况下在 android 中使用 native 库时减小 .apk 文件的大小

java - 使数据模型类可序列化

java - spring boot : java. lang.IllegalArgumentException: 对象不是声明类的实例

java - 如何在 JavaFX 中默认检查单选按钮?