8

どこかから来るXMLを解析するためにdom4j1.6.1を使用しています。時々、baliseは名前空間(例えば:)に言及し、時には()に言及しません。そして、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 として定義され、X を表す名前空間にもプレフィックス「es」が付けられている XML を取得していますか? この場合、XML は整形式であり、接頭辞にとらわれず、修飾名を使用して要素を取得するコードが必要です。nullプレフィックスを持つ名前空間を作成すると、一致するURIを持つすべての要素と一致するのか、それともプレフィックスのない要素のみと一致するのかを知るには、Dom4jに精通していませんが、実験する価値はあります.

于 2009-09-14T16:25:36.280 に答える
0

Abhishek として、システム テスト スクリプトで XPath クエリを単純化するために、XML から名前空間を削除する必要がありました。(XML は最初に XSD で検証されます)

私が直面した問題は次のとおりです。

  1. スタックを爆破する傾向のある、深く構造化された 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 に答える