java - JAXB - 转换包含 XSD/命名空间引用的 XML 文件

标签 java jaxb

如何将带有 XSD/命名空间的 XML 转换为对象?

我收到此错误:

javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.opengis.net/wfs/2.0", local:"FeatureCollection"). Expected elements are <{}FeatureCollection>

这是示例 XML:

<wfs:FeatureCollection xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fes="http://www.opengis.net/fes/2.0" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" numberMatched="7961422" numberReturned="0" timeStamp="2019-07-16T09:44:51.540Z" xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://schemas.opengis.net/wfs/2.0/wfs.xsd"/>

我的简单 JAXB 转换器是:

public static Object convertXmlToObject(String xmlString, Class targetClass) {
    JAXBContext jaxbContext;
    try {
        jaxbContext = JAXBContext.newInstance( targetClass);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        return jaxbUnmarshaller.unmarshal(new StringReader(xmlString));
    } catch (JAXBException e) {
        e.printStackTrace();
        return null;
    }
}

该方法的调用是:

TargetObject to = (TargetObject) converter.convertXmlToObject( xmlString, TargetObject.class);

目标对象是:

@XmlRootElement( name="FeatureCollection")
public class TargetObject {
    private long numberMatched = -1;
    private long numberReturned = -1;
    private LocalDateTime timeStamp;
    // ... all getters
    @XmlAttribute
    public void setNumberMatched(long numberMatched) {
        this.numberMatched = numberMatched;
    }
    @XmlAttribute
    public void setNumberReturned(long numberReturned) {
        this.numberReturned = numberReturned;
    }
    @XmlAttribute
    @XmlJavaTypeAdapter(value = LocalDateTimeAdapter.class)
    public void setTimeStamp(LocalDateTime timeStamp) {
        this.timeStamp = timeStamp;
    }
}

如何改进代码以将 XML 字符串转换为对象?

最佳答案

享受这个通用解决方案:

  • 转换器类
  • Junit 测试 - 显示 1 个没有命名空间的测试,但它确实适用于命名空间

使用 XSLT 来剥离 namespace 的转换器类。 JAXB 有时可能过于字面意思,正如您在许多帖子中所读到的那样。

public class XmlToPojo {
    public static Object convertXmlToObject(String xmlString, Class targetClass) {
        JAXBContext jaxbContext;
        try {
            // Step 1 - remove namespaces
            StreamSource xmlSource = new StreamSource(new StringReader(xmlString));
            StreamResult result = new StreamResult(new StringWriter());
            removeNamespace(xmlSource, result);
            // Step 2 - convert XML to object
            jaxbContext = JAXBContext.newInstance(targetClass);
            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
            return jaxbUnmarshaller.unmarshal(new StringReader(result.getWriter().toString()));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Remove namespaces from XML

    private static String xsltNameSpaceRemover = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
            "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n" +
            "\n" +
            "    <xsl:output indent=\"yes\" method=\"xml\" encoding=\"utf-8\" omit-xml-declaration=\"yes\"/>\n" +
            "\n" +
            "    <!-- Stylesheet to remove all namespaces from a document -->\n" +
            "    <!-- NOTE: this will lead to attribute name clash, if an element contains\n" +
            "        two attributes with same local name but different namespace prefix -->\n" +
            "    <!-- Nodes that cannot have a namespace are copied as such -->\n" +
            "\n" +
            "    <!-- template to copy elements -->\n" +
            "    <xsl:template match=\"*\">\n" +
            "        <xsl:element name=\"{local-name()}\">\n" +
            "            <xsl:apply-templates select=\"@* | node()\"/>\n" +
            "        </xsl:element>\n" +
            "    </xsl:template>\n" +
            "\n" +
            "    <!-- template to copy attributes -->\n" +
            "    <xsl:template match=\"@*\">\n" +
            "        <xsl:attribute name=\"{local-name()}\">\n" +
            "            <xsl:value-of select=\".\"/>\n" +
            "        </xsl:attribute>\n" +
            "    </xsl:template>\n" +
            "\n" +
            "    <!-- template to copy the rest of the nodes -->\n" +
            "    <xsl:template match=\"comment() | text() | processing-instruction()\">\n" +
            "        <xsl:copy/>\n" +
            "    </xsl:template>\n" +
            "\n" +
            "</xsl:stylesheet>";

    private static void removeNamespace(Source xmlSource, Result xmlOutput) throws TransformerException {
        TransformerFactory factory = TransformerFactory.newInstance();
        StreamSource xsltSource = new StreamSource(new StringReader(xsltNameSpaceRemover));
        Transformer transformer = factory.newTransformer(xsltSource);
        transformer.transform(xmlSource, xmlOutput);
    }
}

一个简单的 Junit 测试:

public class XmlToPojoTest {
    @Test
    public void testBasic() {
        String xmlString = "<employee>" +
                "    <department>" +
                "        <id>101</id>" +
                "        <name>IT-ABC</name>" +
                "    </department>" +
                "    <firstName>JJ</firstName>" +
                "    <id>1</id>" +
                "    <lastName>JoHo</lastName>" +
                "</employee>";
        XmlToPojo xmlToPojo = new XmlToPojo();
        Employee emp = (Employee) xmlToPojo.convertXmlToObject(xmlString, Employee.class);
        assertEquals("JJ", emp.getFirstName());
        assertEquals("IT-ABC", emp.getDepartment().getName());
    }
}

关于java - JAXB - 转换包含 XSD/命名空间引用的 XML 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57060022/

相关文章:

java - 如何根据视角修改 Java 编辑器的上下文菜单?

java - 为什么 System.out::println 比 Java 8 中的匿名类实现慢?

java - JAXB:JAXB 的问题

java - 将 XML 属性的值限制为不同属性/元素的值

Java的InputStream会破坏信息,为什么?

java - BeanDefinitionStoreException 运行服务器时无法读取候选组件类

java - 将 java 对象转换为 XML REST 响应

java - 继承和 JAXB 如何协同工作?

jaxb - 我如何告诉 wsimport 单独的 WSDL 文件引用相同的对象类?

java - 将一个对象的属性转换为另一个对象