40

次のように始まる XML ファイルがあります。

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

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

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

- また -

名前空間が 1 つしかないことを考えると、どうにかして XPath が xml に存在する唯一の名前空間を使用するようにし、常に名前空間を追加することでコードが乱雑になるのを回避します。

4

3 に答える 3

88

あなたが試みるかもしれないいくつかのテクニックがあります。どちらを使用するかは、ドキュメントから取得する必要のある情報、必要な厳密さ、および使用している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をドキュメント要素の1つに対してテストできます。

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

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

于 2008-09-23T18:53:32.460 に答える
12

この 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>
   </xsl:stylesheet>

次の 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>
于 2008-11-14T02:56:27.517 に答える
4

残念ながら、XPath には「デフォルトの名前空間」という概念がありません。XPath コンテキストで接頭辞付きの名前空間を登録し、それらの接頭辞を XPath 式で使用する必要があります。これは非常に冗長な xpath を意味しますが、これは XPath 1 の基本的な欠点です。明らかに XPath 2 はこれに対処しますが、それは今のところ役に立ちません。

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

于 2008-09-23T18:23:41.970 に答える