Java 使用dom4j清理命名空间处理

Java 使用dom4j清理命名空间处理,java,namespaces,dom4j,Java,Namespaces,Dom4j,我们正在使用dom4j1.6.1解析来自某处的XML提交。应答器有时提到名称空间(例如:),有时不提到()。并调用元素。selectSingleNode(字符串s)失败 目前我们有3种解决方案,但我们并不满意 1-在对xml文档执行任何操作之前,请删除所有命名空间 xml = xml .replaceAll("xmlns=\"[^\"]*\"",""); xml = xml .replaceAll("ds:",""); xml = xml .replaceAll("etm:",""); [...

我们正在使用dom4j1.6.1解析来自某处的XML提交。应答器有时提到名称空间(例如:),有时不提到()。并调用元素。selectSingleNode(字符串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)

所以。。。你怎么认为?巫婆一号是不是更糟?您是否有其他解决方案可供建议?

选项1是危险的,因为您无法在不预先解析文档的情况下保证给定名称空间的前缀,并且可能导致名称空间冲突。如果您正在使用一个文档而没有输出任何内容,根据文档的来源,这可能没问题,但否则它会丢失太多信息

选项2可以递归应用,但它会遇到许多与选项1相同的问题

选项3听起来是最好的方法,但与其让代码变得杂乱无章,不如创建一个执行这两项检查的静态方法,而不是在整个代码库中放置相同的if语句


最好的方法是让发送坏XML的人来修复它。当然,这就引出了一个问题:它是否真的坏了。具体地说,您得到的是默认名称空间定义为X,然后表示X的名称空间的前缀为“es”的XML吗?如果是这种情况,那么XML是格式良好的,您只需要不知道前缀的代码,但仍然使用限定名来获取元素。我对Dom4j还不太熟悉,不知道创建一个带有空前缀的名称空间是否会使它匹配所有具有匹配URI的元素,还是只匹配那些没有前缀的元素,但这是值得尝试的。

下面是我找到并现在使用的一些代码。如果寻找一种通用方法,从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);
            }            
        }
    }
publicstaticvoidremoveallnamespace(documentdoc){
元素根=doc.getRootElement();
如果(root.getNamespace()=
名称空间。没有名称空间){
removeNamespaces(root.content());
}
}
公共静态命名空间(文档文档,命名空间原始){
元素根=doc.getRootElement();
如果(原始!=null){
setNamespaces(root.content(),原始);
}
}
公共静态void setNamespace(元素元素,命名空间ns){
elem.setQName(QName.get)(elem.getName(),ns,
elem.getQualifiedName());
}
/**
*递归删除元素的名称空间及其所有
子项:设置为Namespace.NO_Namespace
*/
公共静态void removeNamespaces(元素elem){
setNamespaces(elem,Namespace.NO_Namespace);
}
/**
*递归删除列表的名称空间及其所有
子项:设置为Namespace.NO_Namespace
*/
公共静态void removeNamespaces(列表l){
setNamespaces(l,Namespace.NO_Namespace);
}
/**
*递归地设置元素及其所有子元素的名称空间。
*/
公共静态void setnamespace(元素元素,命名空间ns){
setNamespace(elem,ns);
setNamespaces(elem.content(),ns);
}
/**
*递归设置列表和所有子项的名称空间(如果
当前命名空间不匹配
*/
公共静态void setnamespace(列表l,命名空间ns){
节点n=null;
对于(int i=0;i

希望这对有需要的人有用

我想删除任何名称空间信息(声明和标记),以简化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);
        }
         }
 }

作为Abhishek,我需要从XML中剥离名称空间,以简化系统测试脚本中的XPath查询。(XML首先经过XSD验证)

以下是我面临的问题:

  • 我需要处理深层结构的XML,这些XML有可能会破坏堆栈
  • 对于大多数复杂的XML,由于我没有充分研究的原因,只有在首先遍历DOM树深度时才能可靠地剥离所有名称空间。这样就排除了访问者,或者使用
    文档获取节点列表。选择节点(“//*”)
  • 我最终得出以下结论(不是最优雅的,但如果这有助于解决某人的问题……):


    此代码实际上可以工作:

    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();
        }
    }
    

    我将尝试挖掘带有空前缀的关于名称空间的文档。无论如何,谢谢你。关于XML文件的来源:它们不是改变任何东西的方式。但是包含或不包含命名空间的文件都是有效的。通过这些文件,我们构建了我们在系统中使用的对象。但我们从不“写”东西。(我们无权修改xml文件)无法使此代码正常工作。我使用了来自w3schools的xml名称空间示例,但dom4j似乎无法识别名称空间。第一个if(root.getNamespace()!=Namespace.NO_Namespace)的计算结果为true,即使我删除了if,它仍然不起任何作用。嗨,丹,这确实会从文档中删除名称空间。可能您也对删除前缀感兴趣。对不起,在完成我想写的内容之前,我错误地保存了前缀!Dan,此函数会从文档中删除名称空间。我尝试了W3学校的第五个例子。您可以通过创建类似于“//table”的xpath来验证这一点。在文档上运行此xpath
    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();
        }
    }