web-dev-qa-db-ja.com

Xpathを使用してXMLファイルの名前空間を取得する方法

次のようなXMLファイルがあります。

<Elements name="Entities" xmlns="XS-GenerationToolElements">

これらのファイルをたくさん開く必要があります。これらはそれぞれ名前空間が異なりますが、一度に1つの名前空間しかありません(1つのxmlファイルで2つの名前空間が定義されていることはありません)。

XPathを使用して、指定された名前空間を名前空間マネージャーに自動的に追加する方法があります。これまでのところ、xmlファイルを解析して名前空間を取得することしかできませんでしたが、XPathNavigatorインスタンスがあり、名前空間を取得するためのすてきな方法が必要です。

-OR-

名前空間が1つしかない場合、どういうわけかXPathはxmlに存在する唯一の名前空間を使用するようにして、常に名前空間を追加することでコードが乱雑になるのを防ぎます。

37
Luis Filipe

あなたが試すかもしれないいくつかのテクニックがあります。どちらを使用するかは、ドキュメントから取得する必要のある情報、厳密になりたいこと、および使用しているXPath実装がどの程度準拠しているかによって異なります。

特定のプレフィックスに関連付けられた名前空間URIを取得する1つの方法は、_namespace::_軸を使用することです。これにより、名前がプレフィックスで値が名前空間URIである名前空間ノードが得られます。たとえば、次のパスを使用して、ドキュメント要素のデフォルトの名前空間URIを取得できます。

_/*/namespace::*[name()='']
_

これを使用して、XPathNavigatorの名前空間の関連付けを設定できる場合があります。ただし、_namespace::_軸は、常に実装されているわけではないXPath 1.0のコーナーの1つであることに注意してください。

その名前空間URIを取得する2番目の方法は、ドキュメント要素でnamespace-uri()関数を使用することです(これは、常にその名前空間にあると言われています)。表現:

_namespace-uri(/*)
_

その名前空間を与えます。

別の方法としては、接頭辞とその名前空間の関連付けを忘れて、パスを名前空間なしにするだけです。名前空間がわからない要素を参照する必要があるときはいつでも、local-name()関数を使用してこれを行うことができます。例えば:

_//*[local-name() = 'Element']
_

本当に必要な場合は、さらに一歩進んで、要素の名前空間URIをドキュメント要素のURIに対してテストできます。

_//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]
_

名前空間が意味をなさないように思われる場合の最後のオプションは、名前空間を取り除くフィルターを介してXMLを実行することです。そうすれば、XPathでそれらについて心配する必要はまったくありません。これを行う最も簡単な方法は、正規表現でxmlns属性を削除することですが、同時に他の整頓を行う必要がある場合は、さらに複雑なことを行うことができます。

83
JeniT

この40行のxslt変換は、指定されたXMLドキュメントの名前空間に関するすべての有用な情報を提供します

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:ext="http://exslt.org/common"
   exclude-result-prefixes="ext"
>

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:key name="kNsByNsUri" match="ns" use="@uri"/>

<xsl:variable name="vXmlNS" 
    select="'http://www.w3.org/XML/1998/namespace'"/>

<xsl:template match="/">
  <xsl:variable name="vrtfNamespaces">
    <xsl:for-each select=
      "//namespace::*
             [not(. = $vXmlNS)
             and
              . = namespace-uri(..)
           ]">
      <ns element="{name(..)}"
          prefix="{name()}" uri="{.}"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vNamespaces"
    select="ext:node-set($vrtfNamespaces)/*"/>

  <namespaces>
          <xsl:for-each select=
           "$vNamespaces[generate-id()
                        =
                         generate-id(key('kNsByNsUri',@uri)[1])
                        ]">
            <namespace uri="{@uri}">
              <xsl:for-each select="key('kNsByNsUri',@uri)/@element">
                <element name="{.}" prefix="{../@prefix}"/>
              </xsl:for-each>
            </namespace>
          </xsl:for-each>
  </namespaces>
</xsl:template>

次のXMLドキュメントに適用した場合:

<a xmlns="my:def1" xmlns:n1="my:n1"
   xmlns:n2="my:n2" xmlns:n3="my:n3">
  <b>
    <n1:d/>
  </b>
  <n1:c>
    <n2:e>
      <f/>
    </n2:e>
  </n1:c>
  <n2:g/>
</a>

必要な結果が生成されます:

<namespaces>
   <namespace uri="my:def1">
      <element name="a" prefix=""/>
      <element name="b" prefix=""/>
      <element name="f" prefix=""/>
   </namespace>
   <namespace uri="my:n1">
      <element name="n1:d" prefix="n1"/>
      <element name="n1:c" prefix="n1"/>
   </namespace>
   <namespace uri="my:n2">
      <element name="n2:e" prefix="n2"/>
      <element name="n2:g" prefix="n2"/>
   </namespace>
</namespaces>
10

残念ながら、XPathには「デフォルト名前空間」の概念がありません。 XPathコンテキストでプレフィックスを使用して名前空間を登録し、それらのプレフィックスをXPath式で使用する必要があります。これは非常に冗長なxpathを意味しますが、これはXPath 1の基本的な欠点です。どうやらXPath 2がこれに対処しますが、現時点ではそれは役に立ちません。

プログラムで名前空間のXMLドキュメントを調べ、その名前空間をXPathコンテキストのプレフィックスに関連付けてから、xpath式でプレフィックスを使用することをお勧めします。

4
skaffman