从功能上来说,这两个 block 应该是相同的
<soapenv:Body>
<ns1:login xmlns:ns1="urn:soap.sof.com">
<userInfo>
<username>superuser</username>
<password>qapass</password>
</userInfo>
</ns1:login>
</soapenv:Body>
-----------------------
<soapenv:Body>
<ns1:login xmlns:ns1="urn:soap.sof.com">
<ns1:userInfo>
<ns1:username>superuser</ns1:username>
<ns1:password>qapass</ns1:password>
</ns1:userInfo>
</ns1:login>
</soapenv:Body>
但是,当我使用 AXIS2 阅读并且也使用 java6 对其进行测试时,我遇到了问题。
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage soapMsg = factory.createMessage(new MimeHeaders(), SimpleTest.class.getResourceAsStream("LoginSoap.xml"));
SOAPBody body = soapMsg.getSOAPBody();
NodeList nodeList = body.getElementsByTagNameNS("urn:soap.sof.com", "login");
System.out.println("Try to get login element" + nodeList.getLength()); // I can get the login element
Node item = nodeList.item(0);
NodeList elementsByTagNameNS = ((Element)item).getElementsByTagNameNS("urn:soap.sof.com", "username");
System.out.println("try to get username element " + elementsByTagNameNS.getLength());
因此,如果我将第二个 getElementsByTagNameNS 替换为 ((Element)item).getElementsByTagName("username");,我就能够获取用户名元素。即使用户名没有前缀,是否也没有 ns1 命名空间?我是否应该跟踪 namespace 范围来读取元素?如果我的 xml 元素有多深,那不是会变得很糟糕吗?有没有一种解决方法可以让我在不知道是否定义了前缀的情况下读取 ns1 命名空间中的元素?
最佳答案
简短的回答是否定的,这些文件不一样。命名空间不会被元素继承,并且在设置前缀时,您的命名空间不再充当文档的默认命名空间。
这两个是相同的:
<soapenv:Body>
<login xmlns="urn:soap.sof.com">
<userInfo>
<username>superuser</username>
<password>qapass</password>
</userInfo>
</login>
</soapenv:Body>
-----------------------
<soapenv:Body>
<ns1:login xmlns:ns1="urn:soap.sof.com">
<ns1:userInfo>
<ns1:username>superuser</ns1:username>
<ns1:password>qapass</ns1:password>
</ns1:userInfo>
</ns1:login>
</soapenv:Body>
为了以更可靠的方式阅读文档,您可能应该考虑编译一些 XPath 语句。命名空间问题只是依赖 getElementsByTagName(NS) 便捷方法的问题之一。
-- 编辑--
Xpath 本身非常基础。例如,//userInfo
选择任意级别的所有 userInfo 元素。 //login/userInfo
选择作为任意级别登录的子级的所有 userInfo 元素。与其他事情一样,当您必须开始添加 namespace 时,事情会变得更加困惑。
private NamespaceContext ns = new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if (prefix.equals("urn") return "urn:soap.sof.com";
else return XMLConstants.NULL_NS_URI;
}
public String getPrefix(String namespace) {
throw new UnsupportedOperationException();
}
public Iterator getPrefixes(String namespace) {
throw new UnsupportedOperationException();
}};
XPathFactory xpfactory = XPathFactory.newInstance();
XPath xpath = xpfactory.newXPath();
xpath.setNamespaceContext(ns);
NodeList nodes = (NodeList) xpath.evaluate("//urn:userInfo|//userInfo", myDom, XPathConstants.NODESET);
//find all userInfo at any depth with either namespace.
自从我使用 JAXP 以来已经很长时间了,但我认为这基本上是正确的。运行 xPath 并不慢,但编译它们却很慢。您可以将它们编译为 XPathExpression 以提高性能,但它们不是线程安全的,因此您不能仅将它们缓存在 servlet 上。从来都不容易=/。
如果您要处理大量 XML,我建议使用 Jaxen 而不是 JAXP。 (另一方面,如果您只做很少的 XML 并且它只是前端的一个,也许 getElementsByTagName 并不是最糟糕的事情:))
关于Java读取没有前缀但在命名空间范围内的xml元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2830624/