Ant*_*val 8 java namespaces dom4j
我们正在使用dom4j 1.6.1来解析来自某个地方的XML.有时,应答器提到了命名空间(例如:),有时候没有().并且它调用Element.selectSingleNode(String s)失败.
目前我们有3个解决方案,我们对它们不满意
1 - 在对xml文档执行任何操作之前删除所有命名空间
xml = xml .replaceAll("xmlns=\"[^\"]*\"","");
xml = xml .replaceAll("ds:","");
xml = xml .replaceAll("etm:","");
[...] // and so on for each kind of namespace
Run Code Online (Sandbox Code Playgroud)
2 - 在获取节点之前删除命名空间通过调用
Element.remove(Namespace ns)
Run Code Online (Sandbox Code Playgroud)
但它仅适用于节点和第一级子节点
3 - 使代码混乱
node = rootElement.selectSingleNode(NameWithoutNameSpace)
if ( node == null )
node = rootElement.selectSingleNode(NameWithNameSpace)
Run Code Online (Sandbox Code Playgroud)
所以你怎么看 ?女巫一个是不是更糟糕?你有其他解决方案吗?
我想删除任何名称空间信息(声明和标记)以简化xpath评估.我最终得到了这个解决方案:
String xml = ...
SAXReader reader = new SAXReader();
Document document = reader.read(new ByteArrayInputStream(xml.getBytes()));
document.accept(new NameSpaceCleaner());
return document.asXML();
Run Code Online (Sandbox Code Playgroud)
NameSpaceCleaner是dom4j访问者:
private static final class NameSpaceCleaner extends VisitorSupport {
public void visit(Document document) {
((DefaultElement) document.getRootElement())
.setNamespace(Namespace.NO_NAMESPACE);
document.getRootElement().additionalNamespaces().clear();
}
public void visit(Namespace namespace) {
namespace.detach();
}
public void visit(Attribute node) {
if (node.toString().contains("xmlns")
|| node.toString().contains("xsi:")) {
node.detach();
}
}
public void visit(Element node) {
if (node instanceof DefaultElement) {
((DefaultElement) node).setNamespace(Namespace.NO_NAMESPACE);
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是我发现并现在使用的一些代码。如果要寻找通用方法,从dom4j文档中删除所有名称空间可能很有用。
public static void removeAllNamespaces(Document doc) {
Element root = doc.getRootElement();
if (root.getNamespace() !=
Namespace.NO_NAMESPACE) {
removeNamespaces(root.content());
}
}
public static void unfixNamespaces(Document doc, Namespace original) {
Element root = doc.getRootElement();
if (original != null) {
setNamespaces(root.content(), original);
}
}
public static void setNamespace(Element elem, Namespace ns) {
elem.setQName(QName.get(elem.getName(), ns,
elem.getQualifiedName()));
}
/**
*Recursively removes the namespace of the element and all its
children: sets to Namespace.NO_NAMESPACE
*/
public static void removeNamespaces(Element elem) {
setNamespaces(elem, Namespace.NO_NAMESPACE);
}
/**
*Recursively removes the namespace of the list and all its
children: sets to Namespace.NO_NAMESPACE
*/
public static void removeNamespaces(List l) {
setNamespaces(l, Namespace.NO_NAMESPACE);
}
/**
*Recursively sets the namespace of the element and all its children.
*/
public static void setNamespaces(Element elem, Namespace ns) {
setNamespace(elem, ns);
setNamespaces(elem.content(), ns);
}
/**
*Recursively sets the namespace of the List and all children if the
current namespace is match
*/
public static void setNamespaces(List l, Namespace ns) {
Node n = null;
for (int i = 0; i < l.size(); i++) {
n = (Node) l.get(i);
if (n.getNodeType() == Node.ATTRIBUTE_NODE) {
((Attribute) n).setNamespace(ns);
}
if (n.getNodeType() == Node.ELEMENT_NODE) {
setNamespaces((Element) n, ns);
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望这对需要它的人有用!
选项 1 很危险,因为如果不预先解析文档,就无法保证给定命名空间的前缀,并且最终可能会导致命名空间冲突。如果您正在使用文档并且不输出任何内容,则可能没问题,具体取决于文档的来源,但否则它只会丢失太多信息。
选项 2 可以递归应用,但它有许多与选项 1 相同的问题。
选项 3 听起来是最好的方法,但不要让代码变得混乱,而是创建一个执行这两项检查的静态方法,而不是在整个代码库中放置相同的 if 语句。
最好的方法是让向您发送错误 XML 的人修复它。当然,这引出了一个问题:它是否真的坏了。具体来说,您是否正在获取默认命名空间定义为 X 的 XML,然后为代表 X 的命名空间赋予前缀“es”?如果是这种情况,则 XML 格式良好,您只需要与前缀无关的代码,但仍使用限定名称来获取元素。我对 Dom4j 不太熟悉,不知道创建带有 null 前缀的命名空间是否会导致它匹配具有匹配 URI 的所有元素或仅匹配那些没有前缀的元素,但它值得尝试。