8

我们正在使用 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)

所以你怎么看 ?女巫一号是不是越差?您还有其他解决方案要提出吗?

4

5 回答 5

6

我想删除任何命名空间信息(声明和标签)以简化 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);
        }
         }
 }
于 2011-08-18T12:03:57.670 回答
5

以下是我找到并现在使用的一些代码。如果寻找一种通用的方式,从 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);
            }            
        }
    }

希望这对需要它的人有用!

于 2010-08-26T08:05:15.537 回答
1

选项 1 很危险,因为您无法在不预先解析文档的情况下保证给定名称空间的前缀,并且您最终可能会遇到名称空间冲突。如果您正在使用文档而不输出任何内容,则可能没问题,具体取决于文档的来源,但否则只会丢失太多信息。

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

选项 3 听起来是最好的方法,但与其使代码混乱,不如创建一个同时进行检查的静态方法,而不是在整个代码库中放置相同的 if 语句。

最好的方法是让向您发送错误 XML 的人来修复它。当然,这引出了一个问题,它是否真的坏了。具体来说,您是否正在获取默认命名空间定义为 X 的 XML,然后为同样代表 X 的命名空间赋予前缀“es”?如果是这种情况,则 XML 格式正确,您只需要与前缀无关的代码,但仍使用限定名称来获取元素。我对 Dom4j 不够熟悉,不知道创建具有空前缀的命名空间是否会导致它匹配具有匹配 URI 的所有元素或仅匹配没有前缀的元素,但值得尝试。

于 2009-09-14T16:25:36.280 回答
0

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

以下是我遇到的问题:

  1. 我需要处理结构很深的 XML,这种 XML 往往会导致堆栈崩溃。
  2. 在大多数复杂的 XML 上,由于我没有完全调查的原因,剥离所有命名空间只有在首先遍历 DOM 树深度时才能可靠地工作。这样就排除了访问者,或者获取了节点列表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);
    }
}
于 2013-03-23T01:54:00.807 回答
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();
    }
}
于 2014-11-15T08:05:22.780 回答