3

XPath式のマッピングからXMLテンプレートファイルを入力(または生成)するための最良の方法は何でしょうか?

要件は、テンプレートから開始する必要があることです(これには、XPath式でキャプチャされない情報が含まれている可能性があるため)。

たとえば、開始テンプレートは次のようになります。

<s11:Envelope xmlns:s11='http://schemas.xmlsoap.org/soap/envelope/'>
    <ns1:create xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/'>
      <article xmlns:ns1='http://predic8.com/material/1/'>
        <name>?XXX?</name>
        <description>?XXX?</description>
        <price xmlns:ns1='http://predic8.com/common/1/'>
          <amount>?999.99?</amount>
          <currency xmlns:ns1='http://predic8.com/common/1/'>???</currency>
        </price>
        <id xmlns:ns1='http://predic8.com/material/1/'>???</id>
      </article>
    </ns1:create>
  </s11:Body>
</s11:Envelope>

次に、次のようなものが提供されます。

expression: /create/article[1]/id                => 1
expression: /create/article[1]/description       => bar
expression: /create/article[1]/name[1]           => foo
expression: /create/article[1]/price[1]/amount   => 00.00
expression: /create/article[1]/price[1]/currency => USD
expression: /create/article[2]/id                => 2
expression: /create/article[2]/description       => some name
expression: /create/article[2]/name[1]           => some description
expression: /create/article[2]/price[1]/amount   => 00.01
expression: /create/article[2]/price[1]/currency => USD

次に、以下を生成する必要があります。

<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>bar</description>
        <price xmlns:ns1='http://predic8.com/common/1/'>
            <amount>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>some name</name>
        <description>some description</description>
        <price xmlns:ns1='http://predic8.com/common/2/'>
            <amount>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>

私はJavaで実装されていますが、可能であればXSLTベースのソリューションをお勧めします。

PS:この質問は、私が最近尋ねた別の質問の逆です。

4

2 に答える 2

3

この変換は、必要な結果の構造を持つ XML ドキュメントを「式」から作成します。この結果を最終的な結果に変換する必要があります。

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vPop" as="element()*">
    <item path="/create/article[1]/id">1</item>
    <item path="/create/article[1]/description">bar</item>
    <item path="/create/article[1]/name[1]">foo</item>
    <item path="/create/article[1]/price[1]/amount">00.00</item>
    <item path="/create/article[1]/price[1]/currency">USD</item>
    <item path="/create/article[1]/price[2]/amount">11.11</item>
    <item path="/create/article[1]/price[2]/currency">AUD</item>
    <item path="/create/article[2]/id">2</item>
    <item path="/create/article[2]/description">some name</item>
    <item path="/create/article[2]/name[1]">some description</item>
    <item path="/create/article[2]/price[1]/amount">00.01</item>
    <item path="/create/article[2]/price[1]/currency">USD</item>
 </xsl:variable>

 <xsl:template match="/">
  <xsl:sequence select="my:subTree($vPop/@path/concat(.,'/',string(..)))"/>
 </xsl:template>

 <xsl:function name="my:subTree" as="node()*">
  <xsl:param name="pPaths" as="xs:string*"/>

  <xsl:for-each-group select="$pPaths"
    group-adjacent=
        "substring-before(substring-after(concat(., '/'), '/'), '/')">
    <xsl:if test="current-grouping-key()">
     <xsl:choose>
       <xsl:when test=
          "substring-after(current-group()[1], current-grouping-key())">
         <xsl:element name=
           "{substring-before(concat(current-grouping-key(), '['), '[')}">

          <xsl:sequence select=
            "my:subTree(for $s in current-group()
                         return
                            concat('/',substring-after(substring($s, 2),'/'))
                             )
            "/>
        </xsl:element>
       </xsl:when>
       <xsl:otherwise>
        <xsl:value-of select="current-grouping-key()"/>
       </xsl:otherwise>
     </xsl:choose>
     </xsl:if>
  </xsl:for-each-group>
 </xsl:function>
</xsl:stylesheet>

この変換が任意の XML ドキュメント (使用されていない) に適用されると、結果は次のようになります。

<create>
   <article>
      <id>1</id>
      <description>bar</description>
      <name>foo</name>
      <price>
         <amount>00.00</amount>
         <currency>USD</currency>
      </price>
      <price>
         <amount>11.11</amount>
         <currency>AUD</currency>
      </price>
   </article>
   <article>
      <id>2</id>
      <description>some name</description>
      <name>some description</name>
      <price>
         <amount>00.01</amount>
         <currency>USD</currency>
      </price>
   </article>
</create>

:

  1. 与えられた「式」を、この変換で使用される形式に変換する必要があります。これは簡単で簡単です。

  2. 最終的な変換では、すべてのノードを「そのまま」(アイデンティティ ルールを使用して) コピーする必要がありますが、トップ ノードは"http://predic8.com/wsdl/material/ArticleService/1/"名前空間で生成する必要があります。「テンプレート」に存在する他の名前空間は使用されず、安全に省略できることに注意してください。

于 2012-07-08T23:31:20.433 に答える
0

このソリューションでは、XPATH 入力情報を少し再編成し、2 段階の変換を許可する必要があります。最初の変換でスタイルシートが作成され、2 番目の変換で実行されます。したがって、クライアントは XSLT エンジンを 2 回呼び出す必要があります。問題がある場合はお知らせください。

第一歩

このように、XPATH 情報を XML ドキュメントに再編成してください。難しいことではなく、XSLT スクリプトでさえもこの作業を行うように記述できます。

<paths>
 <rule>
  <match>article[1]/id[1]</match>
  <namespaces>
   <namespace prefix="ns1">http://predic8.com/wsdl/material/ArticleService/1/</namespace>
   <!-- The namespace node declares a namespace that is used in the match expression.
        There can be many of these. It is not required to define the s11: namespace,
        nor the ns1 namespace. -->
  </namespaces>
  <replacement>1</replacement>
 </rule> 
 <rule>
  <match>article[1]/description[1]</match>
  <namespaces/>
  <replacement>bar</replacement>
 </rule>
 ... etc ...
</paths>

解の制約

上記の規則文書では、次のように制約されています。

  1. 一致には暗黙的に接頭辞「expression: /create/」が付きます。それを明示的に入れないでください。
  2. すべての一致は article[n] のように開始する必要があります。n は序数です。
  3. ルールをゼロにすることはできません。
  4. s11="http://schemas.xmlsoap.org/soap/envelope/" および ns1="http://predic8.com/wsdl/material/ArticleService/1/" 以外の、一致で使用するプレフィックス. (注:名前空間が「/」で終わるのは有効ではないと思いますが、それについてはわかりません)、名前空間ノードで定義されています。

上記は、ステップ 1 の変換への入力ドキュメントです。このドキュメントをこのスタイルシートに適用...

<xsl:stylesheet version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
      xmlns:step2="http://www.w3.org/1999/XSL/Transform-step2"
      xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/"                       
      xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes='xsl'>
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:namespace-alias stylesheet-prefix="step2" result-prefix="xsl"/>

<xsl:template match="/">
 <step2:stylesheet version="2.0">
 <step2:output method="xml" indent="yes" encoding="UTF-8" />

  <step2:variable name="replicated-template" as="element()*">
   <step2:apply-templates select="/" mode="replication" />
  </step2:variable>

  <step2:template match="@*|node()" mode="replication">
     <step2:copy>
        <step2:apply-templates select="@*|node()" mode="replication" />
     </step2:copy>
  </step2:template>

  <step2:template match="/s11:Envelope/s11:Body/ns1:create/article" mode="replication">
   <step2:variable name="replicant" select="." />  
    <step2:for-each select="for $i in 1 to
       {max(for $m in /paths/rule/match return
        xs:integer(substring-before(substring-after($m,'article['),']')))}
          return $i">
   <step2:for-each select="$replicant">
       <step2:copy>
        <step2:apply-templates select="@*|node()" mode="replication" />
       </step2:copy>
      </step2:for-each>   
     </step2:for-each>    
  </step2:template>

  <step2:template match="@*|node()">
   <step2:copy>
    <step2:apply-templates select="@*|node()"/>
   </step2:copy>
  </step2:template> 

  <step2:template match="/">
   <step2:apply-templates select="$replicated-template" />
  </step2:template>

  <xsl:apply-templates select="paths/rule" /> 
 </step2:stylesheet>
</xsl:template>

<xsl:template match="rule">
 <step2:template match="s11:Envelope/s11:Body/ns1:create/{match}">
  <xsl:for-each select="namespaces/namespace">
   <xsl:namespace name="{@prefix}" select="." />
  </xsl:for-each>
  <step2:copy>
   <step2:apply-templates select="@*"/>
   <step2:value-of select="'{replacement}'"/>
   <step2:apply-templates select="*"/>
  </step2:copy>
 </step2:template>
</xsl:template>

</xsl:stylesheet>

ステップ2

ステップ 1 で出力されたスタイルシートに、入力ドキュメントとして SOAP エンベロープ ファイルを適用します。その結果、必要に応じて変更された元の SOAP ドキュメントが作成されます。これは、ステップ 2 のスタイルシートのサンプルです。説明を簡単にするために、最初のルール (/create/article[1]/id => 1) のみを考慮しています。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:s11="http://schemas.xmlsoap.org/soap/envelope/"
                version="2.0">
   <xsl:output method="xml" indent="yes" encoding="UTF-8"/>
   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
   <xsl:template xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/"
                 match="/s11:Envelope/s11:Body/ns1:create[1]/article[1]/id[1]">
      <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xsl:value-of select="'1'"/>
         <xsl:apply-templates select="*"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

その他の解の制約

  1. テンプレート ドキュメントには、少なくとも 1 つの /s11:Envelope/s11:Body/ns1:create/article が含まれている必要があります。ルールの要求に従って、アーティクル ノードのみが (深く) レプリケートされます。それ以外はどんな構造でも構いません。
  2. テンプレート ドキュメントには、s11:Envelope/s11:Body/ns1:create ノードのネストされたレベルを含めることはできません。

説明

XPATH 式がテンプレートの一致条件からそれほど離れていないことに気付くでしょう。したがって、XPATH と置換値をテンプレート ルールとして再表現するスタイルシートを作成することはそれほど難しくありません。スタイルシートを書くときに、 xsl:namespace-alias を使用すると、「xsl:」を命令として、「xsl:」を意図した出力として明確にすることができます。XSLT 3.0 が登場すると、このアルゴリズムを 1 つのステップに削減できる可能性が高くなります。動的な XPATH 評価が可能になるためです。これが実際の問題の核心です。しかし、当面は 2 段階のプロセスに満足する必要があります。

2 番目のスタイルシートは、2 フェーズの変換です。最初の段階では、ルールで必要な回数だけ、記事レベルからテンプレートを複製します。2 番目のフェーズでは、この複製されたテンプレートを解析し、XPATH によって示されるテキスト値を置換する動的ルールを適用します。


アップデート

私の元の投稿は間違っていました。エラーを指摘してくれた Dimitre に感謝します。上記の更新されたソリューションを見つけてください。

結果論

2 段階のソリューションでは複雑すぎて、wintel プラットフォームで実行している場合は、Saxon の商用バージョンの購入を検討してください。商用版には動的な XPATH 評価機能があると思います。私は商用版を持っていないので、そのような解決策を提供することはできません. evaluate() 関数を使用したソリューションの方がずっと簡単だと思います。XSLT は私にとって単なる趣味です。しかし、ビジネス目的で XSLT を使用している場合、価格は手頃な価格です。

于 2012-07-09T02:00:17.703 に答える