我们正在使用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
2 - 在获取节点之前删除命名空间 致电
Element.remove(Namespace ns)
但它仅适用于节点和子级的第一级
3 - 通过
混乱代码node = rootElement.selectSingleNode(NameWithoutNameSpace)
if ( node == null )
node = rootElement.selectSingleNode(NameWithNameSpace)
那么......你怎么看?女巫一个是不是更糟糕?你有其他解决方案吗?
答案 0 :(得分:5)
我想删除任何名称空间信息(声明和标记)以简化xpath评估。我最终得到了这个解决方案:
String xml = ...
SAXReader reader = new SAXReader();
Document document = reader.read(new ByteArrayInputStream(xml.getBytes()));
document.accept(new NameSpaceCleaner());
return document.asXML();
其中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);
}
}
}
答案 1 :(得分:4)
以下是我找到并现在使用的一些代码。如果寻找通用方法,可能会从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);
}
}
}
希望这对需要它的人有用!
答案 2 :(得分:1)
选项1是危险的,因为如果不预先解析文档而无法保证给定命名空间的前缀,并且因为最终可能会发生命名空间冲突。如果您正在使用文档而不输出任何内容,则可能没问题,具体取决于文档的来源,但是否则会丢失太多信息。
选项2可以递归应用,但它与选项1有许多相同的问题。
选项3听起来像是最好的方法,但不是混乱你的代码,而是制作一个静态方法来执行两种检查,而不是在整个代码库中放置相同的if语句。
最好的方法是让任何向您发送错误XML的人来修复它。当然这引出了一个问题,它实际上已经破裂了。具体来说,您是否获得XML,其中默认命名空间被定义为X,然后代表X的命名空间被赋予前缀'es'?如果是这种情况,则XML格式正确,您只需要与前缀无关的代码,但仍使用限定名称来获取元素。我对Dom4j不太熟悉,知道创建一个带有空前缀的命名空间是否会使所有元素与匹配的URI匹配,或者只匹配那些没有前缀的元素,但是值得尝试。
答案 3 :(得分:0)
作为Abhishek,我需要从XML中剥离命名空间以简化系统测试脚本中的XPath查询。 (XML首先经过XSD验证)
以下是我遇到的问题:
document.selectNodes("//*")
我最终得到了以下内容(不是最优雅的,但如果这有助于解决某人的问题......):
public static String normaliseXml(final String message) {
org.dom4j.Document document;
document = DocumentHelper.parseText(message);
Queue stack = new LinkedList();
Object current = document.getRootElement();
while (current != null) {
if (current instanceof Element) {
Element element = (Element) current;
Iterator iterator = element.elementIterator();
if (iterator.hasNext()) {
stack.offer(element);
current = iterator;
} else {
stripNamespace(element);
current = stack.poll();
}
} else {
Iterator iterator = (Iterator) current;
if (iterator.hasNext()) {
stack.offer(iterator);
current = iterator.next();
} else {
current = stack.poll();
if (current instanceof Element) {
stripNamespace((Element) current);
current = stack.poll();
}
}
}
}
return document.asXML();
}
private static void stripNamespace(Element element) {
QName name = new QName(element.getName(), Namespace.NO_NAMESPACE, element.getName());
element.setQName(name);
for (Object o : element.attributes()) {
Attribute attribute = (Attribute) o;
QName attributeName = new QName(attribute.getName(), Namespace.NO_NAMESPACE, attribute.getName());
String attributeValue = attribute.getValue();
element.remove(attribute);
element.addAttribute(attributeName, attributeValue);
}
for (Object o : element.declaredNamespaces()) {
Namespace namespace = (Namespace) o;
element.remove(namespace);
}
}
答案 4 :(得分:0)
此代码实际上有效:
public void visit(Document document) {
((DefaultElement) document.getRootElement())
.setNamespace(Namespace.NO_NAMESPACE);
document.getRootElement().additionalNamespaces().clear();
}
public void visit(Namespace namespace) {
if (namespace.getParent() != null) {
namespace.getParent().remove(namespace);
}
}
public void visit(Attribute node) {
if (node.toString().contains("xmlns")
|| node.toString().contains("xsi:")) {
node.getParent().remove(node);
}
}
public void visit(Element node) {
if (node instanceof DefaultElement) {
((DefaultElement) node).setNamespace(Namespace.NO_NAMESPACE);
node.additionalNamespaces().clear();
}
}