You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将含同名<meta>标签的XML直接映射为POJO?

问题

从API拿到的XML结构是这样的:

<xml>
  <meta-info>
    <meta name="id" content="1234-5678"/>
    <meta name="title" content="test"/>
    <meta name="fileType" content="txt"/>
    <meta name="priority" content="low"/>
  </meta-info>
</xml>

想要直接映射到一个带String id、title、fileType、priority属性的POJO里,试了两种方法都踩坑:

  1. 用Jackson的@XmlPath注解时,抛了UnrecognizedPropertyException,说识别不了meta-info字段;
  2. 换成JAXB反序列化,没报错但所有属性全是null。
解决方案

方案一:用Jackson XML扩展搞定

首先得确保项目里引入了jackson-dataformat-xml依赖,然后修正POJO的注解配置:

方式1:用正确的@XmlPath路径+忽略未知属性

之前的@XmlPath路径写错了,而且没配置忽略未映射的meta-info字段,导致报错。修正后的POJO:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.fasterxml.jackson.dataformat.xml.annotation.XmlPath;

@JacksonXmlRootElement(localName = "xml")
@JsonIgnoreProperties(ignoreUnknown = true) // 忽略没映射的meta-info字段
public class XmlMapTest {
    @XmlPath("meta-info/meta[@name='id']/@content")
    private String id;

    @XmlPath("meta-info/meta[@name='title']/@content")
    private String title;

    @XmlPath("meta-info/meta[@name='fileType']/@content")
    private String fileType;

    @XmlPath("meta-info/meta[@name='priority']/@content")
    private String priority;

    // 别忘了加getter和setter
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getFileType() { return fileType; }
    public void setFileType(String fileType) { this.fileType = fileType; }
    public String getPriority() { return priority; }
    public void setPriority(String priority) { this.priority = priority; }
}

反序列化代码:

import com.fasterxml.jackson.dataformat.xml.XmlMapper;

XmlMapper xmlMapper = new XmlMapper();
XmlMapTest result = xmlMapper.readValue(xmlString, XmlMapTest.class);

方式2:通过辅助类接收meta标签

如果不想用XmlPath,可以定义一个内部类接收单个meta标签,再通过setter分配属性:

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;

@JacksonXmlRootElement(localName = "xml")
public class XmlMapTest {
    private String id;
    private String title;
    private String fileType;
    private String priority;

    @JacksonXmlProperty(localName = "meta")
    @JacksonXmlElementWrapper(localName = "meta-info", useWrapping = true)
    public void setMeta(Meta meta) {
        switch (meta.getName()) {
            case "id":
                this.id = meta.getContent();
                break;
            case "title":
                this.title = meta.getContent();
                break;
            case "fileType":
                this.fileType = meta.getContent();
                break;
            case "priority":
                this.priority = meta.getContent();
                break;
        }
    }

    // getter方法按需添加
    public String getId() { return id; }
    public String getTitle() { return title; }
    public String getFileType() { return fileType; }
    public String getPriority() { return priority; }

    // 内部类,映射单个meta标签
    public static class Meta {
        @JacksonXmlProperty(isAttribute = true)
        private String name;
        @JacksonXmlProperty(isAttribute = true)
        private String content;

        // getter和setter
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getContent() { return content; }
        public void setContent(String content) { this.content = content; }
    }
}

方案二:用JAXB实现映射

JAXB需要正确处理嵌套的meta-info和meta标签,有两种实现方式:

方式1:用适配器转成Map

先写个适配器类,把<meta>标签转成Map:

import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MetaAdapter extends XmlAdapter<MetaAdapter.MetaList, Map<String, String>> {
    public static class MetaList {
        public List<Meta> meta;
    }

    public static class Meta {
        public String name;
        public String content;
    }

    @Override
    public Map<String, String> unmarshal(MetaList metaList) throws Exception {
        Map<String, String> metaMap = new HashMap<>();
        for (Meta meta : metaList.meta) {
            metaMap.put(meta.name, meta.content);
        }
        return metaMap;
    }

    @Override
    public MetaList marshal(Map<String, String> map) throws Exception {
        // 序列化逻辑按需写,这里暂时返回null
        return null;
    }
}

然后修改POJO,用适配器映射:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.Map;

@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlMapTest {
    @XmlJavaTypeAdapter(MetaAdapter.class)
    private Map<String, String> metaInfo;

    // 通过Map获取属性
    public String getId() {
        return metaInfo != null ? metaInfo.get("id") : null;
    }

    public String getTitle() {
        return metaInfo != null ? metaInfo.get("title") : null;
    }

    public String getFileType() {
        return metaInfo != null ? metaInfo.get("fileType") : null;
    }

    public String getPriority() {
        return metaInfo != null ? metaInfo.get("priority") : null;
    }

    public void setMetaInfo(Map<String, String> metaInfo) {
        this.metaInfo = metaInfo;
    }
}

方式2:嵌套类直接映射

直接定义嵌套类对应meta-info和meta标签,然后通过方法匹配属性:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAttribute;
import java.util.List;

@XmlRootElement(name = "xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class XmlMapTest {
    @XmlElement(name = "meta-info")
    private MetaInfo metaInfo;

    // 获取属性的方法
    public String getId() {
        return findMetaContent("id");
    }

    public String getTitle() {
        return findMetaContent("title");
    }

    public String getFileType() {
        return findMetaContent("fileType");
    }

    public String getPriority() {
        return findMetaContent("priority");
    }

    private String findMetaContent(String name) {
        if (metaInfo == null || metaInfo.metas == null) {
            return null;
        }
        for (Meta meta : metaInfo.metas) {
            if (name.equals(meta.name)) {
                return meta.content;
            }
        }
        return null;
    }

    // 嵌套类对应meta-info标签
    public static class MetaInfo {
        @XmlElement(name = "meta")
        private List<Meta> metas;
    }

    // 嵌套类对应meta标签
    public static class Meta {
        @XmlAttribute
        private String name;
        @XmlAttribute
        private String content;

        // getter和setter
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getContent() { return content; }
        public void setContent(String content) { this.content = content; }
    }
}

JAXB的反序列化代码不变:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;

JAXBContext jc = JAXBContext.newInstance(XmlMapTest.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
XmlMapTest xmlResult = (XmlMapTest) unmarshaller.unmarshal(new StringReader(xml));

内容的提问来源于stack exchange,提问作者Frank de la torre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:12:33