5

次の例のXMLファイルがあるとします。

<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
   <article xmlns:ns1='http://predic8.com/material/1/'>
      <name xmlns:ns1='http://predic8.com/material/1/'>foo</name>
      <description xmlns:ns1='http://predic8.com/material/1/'>bar</description>
      <price xmlns:ns1='http://predic8.com/common/1/'>
         <amount xmlns:ns1='http://predic8.com/common/1/'>00.00</amount>
         <currency xmlns:ns1='http://predic8.com/common/1/'>USD</currency>
      </price>
      <id xmlns:ns1='http://predic8.com/material/1/'>1</id>
   </article>
</ns1:create>

これを一連のxpath式にフラット化するための最良の(最も効率的な)方法は何でしょうか。注:名前空間と属性の情報を無視したい。(必要に応じて、これは前処理ステップとして実行することもできます)。

だから私は出力として取得したい:

/create/article/name
/create/article/description
/create/article/price/amount
/create/article/price/currency
/create/article/id

私はJavaで実装しています。

編集: PS、テキストノードにデータがない場合にもこれが必要になる可能性があるため、たとえば、次のようにすると、上記と同じ出力が生成されます。

<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
  <article xmlns:ns1='http://predic8.com/material/1/'>
    <name />
    <description />
    <price xmlns:ns1='http://predic8.com/common/1/'>
      <amount />
      <currency xmlns:ns1='http://predic8.com/common/1/'></currency>
    </price>
    <id xmlns:ns1='http://predic8.com/material/1/'></id>
  </article>
</ns1:create>
4

2 に答える 2

3

XSLT を使用すると、これを非常に簡単に行うことができます。あなたの例を見ると、テキストを含む要素の XPath だけが必要なようです。そうでない場合は、お知らせください。XSLT を更新できます。

同じ名前の兄弟を処理する方法を示すために、新しい入力例を作成しました。この場合、<article>.

XML 入力

<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
    <article xmlns:ns1='http://predic8.com/material/1/'>
        <name xmlns:ns1='http://predic8.com/material/1/'>foo</name>
        <description xmlns:ns1='http://predic8.com/material/1/'>bar</description>
        <price xmlns:ns1='http://predic8.com/common/1/'>
            <amount xmlns:ns1='http://predic8.com/common/1/'>00.00</amount>
            <currency xmlns:ns1='http://predic8.com/common/1/'>USD</currency>
        </price>
        <id xmlns:ns1='http://predic8.com/material/1/'>1</id>
    </article>
    <article xmlns:ns1='http://predic8.com/material/2/'>
        <name xmlns:ns1='http://predic8.com/material/2/'>some name</name>
        <description xmlns:ns1='http://predic8.com/material/2/'>some description</description>
        <price xmlns:ns1='http://predic8.com/common/2/'>
            <amount xmlns:ns1='http://predic8.com/common/2/'>00.01</amount>
            <currency xmlns:ns1='http://predic8.com/common/2/'>USD</currency>
        </price>
        <id xmlns:ns1='http://predic8.com/material/2/'>2</id>
    </article>
</ns1:create>

XSLT1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="text()"/>

    <xsl:template match="*[text()]">
        <xsl:call-template name="genPath"/>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:template>

    <xsl:template name="genPath">
        <xsl:param name="prevPath"/>
        <xsl:variable name="currPath" select="concat('/',local-name(),'[',
        count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
        <xsl:for-each select="parent::*">
            <xsl:call-template name="genPath">
                <xsl:with-param name="prevPath" select="$currPath"/>
            </xsl:call-template>
        </xsl:for-each>
        <xsl:if test="not(parent::*)">
            <xsl:value-of select="$currPath"/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

出力

/create[1]/article[1]/name[1]
/create[1]/article[1]/description[1]
/create[1]/article[1]/price[1]/amount[1]
/create[1]/article[1]/price[1]/currency[1]
/create[1]/article[1]/id[1]
/create[1]/article[2]/name[1]
/create[1]/article[2]/description[1]
/create[1]/article[2]/price[1]/amount[1]
/create[1]/article[2]/price[1]/currency[1]
/create[1]/article[2]/id[1]

アップデート

[text()]XSLT がすべての要素に対して機能するようにするには、単純にから述語を削除しmatch="*[text()]"ます。これにより、すべての要素のパスが出力されます。他の要素 (create、article、price など) を含む要素のパス出力が必要ない場合は、 predicate を追加し[not(*)]ます。更新された例を次に示します。

新しい XML 入力

<ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
    <article xmlns:ns1='http://predic8.com/material/1/'>
        <name />
        <description />
        <price xmlns:ns1='http://predic8.com/common/1/'>
            <amount />
            <currency xmlns:ns1='http://predic8.com/common/1/'></currency>
        </price>
        <id xmlns:ns1='http://predic8.com/material/1/'></id>
    </article>
    <article xmlns:ns1='http://predic8.com/material/2/'>
        <name xmlns:ns1='http://predic8.com/material/2/'>some name</name>
        <description xmlns:ns1='http://predic8.com/material/2/'>some description</description>
        <price xmlns:ns1='http://predic8.com/common/2/'>
            <amount xmlns:ns1='http://predic8.com/common/2/'>00.01</amount>
            <currency xmlns:ns1='http://predic8.com/common/2/'>USD</currency>
        </price>
        <id xmlns:ns1='http://predic8.com/material/2/'>2</id>
    </article>
</ns1:create>

XSLT1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="text()"/>

    <xsl:template match="*[not(*)]">
        <xsl:call-template name="genPath"/>
        <xsl:apply-templates select="node()"/>
    </xsl:template>

    <xsl:template name="genPath">
        <xsl:param name="prevPath"/>
        <xsl:variable name="currPath" select="concat('/',local-name(),'[',
            count(preceding-sibling::*[name() = name(current())])+1,']',$prevPath)"/>
        <xsl:for-each select="parent::*">
            <xsl:call-template name="genPath">
                <xsl:with-param name="prevPath" select="$currPath"/>
            </xsl:call-template>
        </xsl:for-each>
        <xsl:if test="not(parent::*)">
            <xsl:value-of select="$currPath"/>
            <xsl:text>&#xA;</xsl:text>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

出力

/create[1]/article[1]/name[1]
/create[1]/article[1]/description[1]
/create[1]/article[1]/price[1]/amount[1]
/create[1]/article[1]/price[1]/currency[1]
/create[1]/article[1]/id[1]
/create[1]/article[2]/name[1]
/create[1]/article[2]/description[1]
/create[1]/article[2]/price[1]/amount[1]
/create[1]/article[2]/price[1]/currency[1]
/create[1]/article[2]/id[1]

述語を削除する[not(*)]と、出力は次のようになります (要素ごとにパスが出力されます)。

/create[1]
/create[1]/article[1]
/create[1]/article[1]/name[1]
/create[1]/article[1]/description[1]
/create[1]/article[1]/price[1]
/create[1]/article[1]/price[1]/amount[1]
/create[1]/article[1]/price[1]/currency[1]
/create[1]/article[1]/id[1]
/create[1]/article[2]
/create[1]/article[2]/name[1]
/create[1]/article[2]/description[1]
/create[1]/article[2]/price[1]
/create[1]/article[2]/price[1]/amount[1]
/create[1]/article[2]/price[1]/currency[1]
/create[1]/article[2]/id[1]

約 65% 高速化された XSLT の別のバージョンを次に示します。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="text()"/>

    <xsl:template match="*[not(*)]">
        <xsl:for-each select="ancestor-or-self::*">
            <xsl:value-of select="concat('/',local-name(),'[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/>
        </xsl:for-each>
        <xsl:text>&#xA;</xsl:text>
        <xsl:apply-templates select="node()"/>
    </xsl:template>

</xsl:stylesheet>
于 2012-07-02T20:46:20.413 に答える