如何将带有 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/