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

使用JAXB反序列化含<sup>内部标签的XML时内容丢失问题求助

解决JAXB反序列化时丢失嵌套标签内文本的问题

我来帮你搞定这个JAXB反序列化的坑!首先得搞清楚为什么你只能拿到"Watching TV"——你的<Hobbies>标签里嵌套了<sup>子标签,默认的JAXB字符串映射只会提取最后一个独立文本节点,前面的"Playing Cricket "和<sup>里的"+"都被忽略了,只留下了子标签后面的文本。

下面给你两种可行的解决方案,你可以根据需求选择:

方案一:自定义DomHandler递归提取所有文本节点

如果你的需求是把所有文本内容合并(比如得到"Playing Cricket + Watching TV"),可以用这种方法。先写一个能遍历所有节点收集文本的DomHandler:

import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.bind.ValidationEventHandler;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TextExtractingDomHandler implements DomHandler<String, DOMSource> {

    @Override
    public DOMSource createUnmarshaller(ValidationEventHandler errorHandler) {
        return new DOMSource();
    }

    @Override
    public String getElement(DOMSource domSource) {
        Node node = domSource.getNode();
        StringBuilder sb = new StringBuilder();
        collectAllText(node, sb);
        return sb.toString().trim();
    }

    // 递归遍历所有节点,收集文本内容
    private void collectAllText(Node node, StringBuilder sb) {
        if (node.getNodeType() == Node.TEXT_NODE) {
            sb.append(node.getNodeValue().trim()).append(" ");
        }
        NodeList childNodes = node.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            collectAllText(childNodes.item(i), sb);
        }
    }

    @Override
    public Source marshal(String text, ValidationEventHandler errorHandler) {
        // 如果不需要序列化功能,直接抛出异常即可
        throw new UnsupportedOperationException("序列化暂不支持");
    }
}

然后在实体类的hobbies字段上标注使用这个Handler:

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAnyElement;

@XmlRootElement(name = "School")
public class School {
    private Student student;

    // getter和setter方法省略

    public static class Student {
        private String name;
        
        // 用自定义DomHandler处理Hobbies字段
        @XmlAnyElement(TextExtractingDomHandler.class)
        private String hobbies;

        // getter和setter方法省略
    }
}

方案二:用XmlAdapter处理元素节点

这种方法和DomHandler思路类似,但用适配器的方式实现,也能达到同样的效果:

先写一个自定义适配器:

import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class HobbiesTextAdapter extends XmlAdapter<Element, String> {

    @Override
    public String unmarshal(Element element) throws Exception {
        StringBuilder sb = new StringBuilder();
        NodeList nodes = element.getChildNodes();
        for (int i = 0; i < nodes.getLength(); i++) {
            Node node = nodes.item(i);
            if (node.getNodeType() == Node.TEXT_NODE) {
                sb.append(node.getNodeValue().trim()).append(" ");
            } else if (node.getNodeType() == Node.ELEMENT_NODE) {
                // 提取子元素里的文本内容
                sb.append(node.getTextContent().trim()).append(" ");
            }
        }
        return sb.toString().trim();
    }

    @Override
    public Element marshal(String v) throws Exception {
        throw new UnsupportedOperationException("序列化暂不支持");
    }
}

然后在实体类中使用这个适配器:

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

@XmlRootElement(name = "School")
public class School {
    private Student student;

    // getter和setter省略

    public static class Student {
        private String name;
        
        @XmlJavaTypeAdapter(HobbiesTextAdapter.class)
        private String hobbies;

        // getter和setter省略
    }
}

额外需求:保留HTML标签(比如<sup>)

如果你想完整保留<Hobbies>里的HTML标签(得到"Playing Cricket + Watching TV"),可以修改DomHandler,把节点转成XML字符串:

import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.bind.ValidationEventHandler;
import org.w3c.dom.Node;
import java.io.StringWriter;

public class HtmlPreservingDomHandler implements DomHandler<String, DOMSource> {

    @Override
    public DOMSource createUnmarshaller(ValidationEventHandler errorHandler) {
        return new DOMSource();
    }

    @Override
    public String getElement(DOMSource domSource) {
        try {
            Node node = domSource.getNode();
            StringWriter writer = new StringWriter();
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            transformer.transform(new DOMSource(node), new StreamResult(writer));
            return writer.toString().trim();
        } catch (Exception e) {
            throw new RuntimeException("转换节点为字符串失败", e);
        }
    }

    @Override
    public Source marshal(String text, ValidationEventHandler errorHandler) {
        throw new UnsupportedOperationException("序列化暂不支持");
    }
}

然后把实体类里的@XmlAnyElement换成这个Handler就行。

你之前用DomHandler没成功,大概率是因为没有递归处理所有子节点,只读取了直接文本节点,导致前面的内容丢失。按照上面的写法应该就能解决问题啦!

内容的提问来源于stack exchange,提问作者JITEN PATEL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:00:15