3

空の属性を含むバグのある xml があり、空の属性で咳をするパーサーがあります。xml の生成も、空の属性で咳をするパーサーも制御できません。だから私がやりたいのは、すべての空の属性を単純に削除する前処理ステップです。

空の属性を見つけることができましたが、それらを削除する方法がわかりません:

   XPathFactory xpf = XPathFactory.newInstance();
   XPath xpath = xpf.newXPath();
   XPathExpression expr = xpath.compile("//@*");
   Object result = expr.evaluate(d, XPathConstants.NODESET);

   if (result != null) {
    NodeList nodes = (NodeList) result;
    for(int node=0;node<nodes.getLength();node++)
    {
     Node n = nodes.item(node);
     if(isEmpty(n.getTextContent()))
     {
      this.log.warn("Found empty attribute declaration "+n.toString());
      NamedNodeMap parentAttrs = n.getParentNode().getAttributes();
      parentAttrs.removeNamedItem(n.getNodeName());
     }
    }

   } 

このコードは、n.getParentNode().getAttributes() にアクセスするときに NPE を提供します。しかし、要素にアクセスできない場合、要素から空の属性を削除するにはどうすればよいでしょうか?

4

6 に答える 6

3

空の属性のみに制限する場合は、次のXPATHを使用できます。

//*[@*[.='']]

空の属性または空白のみの属性を検索するには、次のようにします。

//*[@*[normalize-space()='']]

このようにして、削除する属性を選択し、空の属性を見つけるためだけにすべての属性をループする必要はありません。

于 2010-03-20T01:43:58.567 に答える
2

次のスタイルシートは、空白のみを含む属性を除いて、ソース ドキュメントのすべてのコンテンツをコピーします。最初のテンプレートは、空の属性を含むすべてを単純にコピーします。ただし、述語を使用しているため、2 番目のテンプレートは最初のテンプレートよりも優先度が高くなります。そのため、空の属性が検出された場合、より一般的な最初のテンプレートに優先して選択されます。この 2 番目のテンプレートは何も生成しません。出力。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="@*[normalize-space()='']"/>
</xsl:stylesheet>
于 2010-03-21T11:14:31.100 に答える
1

とにかく、これはおそらくそれを行う方法ではありません。NodeListから何かを削除しても、XMLからは削除されません。パーサーが実際にすでにロードされているDOMを処理していて、パーサーがそれを取得する前にDOMを操作している場合、これと同様のことが機能する可能性がありますが、最善の戦術ではない可能性があります。

パーサーに向かう途中でXMLFilterを通過させることにより、前処理したほうがよいでしょう。すべての属性を削除するサンプルコードを含むIBMDeveloperworksの記事を見つけました。これは、フィルターのチェーンをパーサーに接続する方法を以前に示したシリーズの一部です

これはすべて、SAXパーサーを使用していることを前提としていますが、それ以外の場合は、ある種の前処理ステップでSAXとそのようなフィルターを使用する方法が考えられます。

xsltで前処理を行うことも可能です。

于 2010-03-19T11:55:01.750 に答える
0

要素ではなく、タイプATTRのノードのみのリスト、または2つの組み合わせのリストを実際に受信していることを確認します。私はXPathExpressionを使用していませんが、パス「// @ *」を「すべての属性」(つまり、私が期待するもの)ではなく「属性を持つ任意の要素」として解釈する場合があります。前者がtrueで、ルートノードに属性がある場合、クエリの結果のノードリストに表示され、定義により[root node] .getParentNode()==nullでNPEが生成されます。

さらに、クエリで属性ノードではなく要素ノードを選択している場合、式n.getTextContent()は、属性値ではなくテキストコンテンツを参照します(ルートノードがリスト、ほとんどのルートノードにはテキストコンテンツがないため)、さらに、属性の削除の試みは何も実行されません(とにかく実際には意図していません)。

したがって、属性ノードではなく要素ノードを受け取っている場合は、属性マップを確認してから変更する必要があります。すべての属性を確認する必要がある場合は、深さ優先探索を記述した方がよい場合があります。 DOMで、そこで変更を実行します。

于 2010-03-19T11:58:02.987 に答える
0

getParentNode() は属性では機能しません。

Attr、Document、DocumentFragment、Entity、および Notation を除くすべてのノードは、親を持つことができます。

100%確実ではありませんが、次の式で属性を持つすべてのノードを選択できると思います:

//*[@*]

その後、属性を簡単にループして、それらが空かどうかを確認できます

于 2010-03-19T11:46:52.543 に答える
0

私は実際にそれを行う方法を見つけました。これで問題が完全に解決するわけではありませんが、今のところ問題ありません。これを使用する場合、値が正確に '' である属性のみをキャッチすることに注意してください。空白のみで構成された値のような他のナンセンスは、これによってキャッチされません。

   XPathFactory xpf = XPathFactory.newInstance();
   XPath xpath = xpf.newXPath();
   XPathExpression expr = xpath.compile("//*[@*='']");
   Object result = expr.evaluate(d, XPathConstants.NODESET);

   if (result != null) {
    NodeList nodes = (NodeList) result;
    for(int node=0;node<nodes.getLength();node++)
    {
     Node n = nodes.item(node);
     NamedNodeMap attrs = n.getAttributes();
     for(int attr=0;attr<attrs.getLength();attr++)
     {
      Node a = attrs.item(attr);
      if(isEmpty(a.getNodeValue()));
      {
       attrs.removeNamedItem(a.getNodeName());
       this.log.warn("Removing empty attribute "+a.toString()+" from element "+n.getNodeName());
      }
     }
    }

   } 

比較のための残念な正規表現は、XSLT 拡張機能としてのみ利用可能であり、すべての XSLT プロセッサでサポートされることは許可されていません :-(

于 2010-03-19T13:06:19.100 に答える