7

XPath マッピングのセットを指定して、完全な XML ファイルを生成できるようにしたいと考えています。

入力は、次の 2 つのマッピングで指定できます。(1) XPath 式と値をリストするマッピング。(2) 適切な名前空間を定義するもう一方。

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

名前空間の場合:

/create               => xmlns:ns1='http://predic8.com/wsdl/material/ArticleService/1/
/create/article       => xmlns:ns1='http://predic8.com/material/1/‘
/create/article/price => xmlns:ns1='http://predic8.com/common/1/‘
/create/article/id    => xmlns:ns1='http://predic8.com/material/1/'

また、XPath属性式も扱うことが重要であることにも注意してください。例: 次のような属性も処理できる必要があります。

/create/article/@type => richtext

最終的な出力は次のようになります。

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

PS: これは以前の質問に対するより詳細な質問ですが、一連のさらなる要件と明確化のために、私のニーズに対応するために、より広範な質問をすることをお勧めしました.

また、これを Java で実装していることにも注意してください。したがって、Java ベースまたは XSLT ベースのソリューションはどちらも完全に受け入れられます。thnx。

さらに注意:私は本当に一般的な解決策を探しています。上記の XML は単なる例です。

4

3 に答える 3

2

この問題は、前の問題の解決策に基づいて構築された場合、簡単な解決策になります。

<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:key name="kNSFor" match="namespace" use="@of"/>
     <xsl:variable name="vStylesheet" select="document('')"/>

     <xsl:variable name="vPop" as="element()*">
        <item path="/create/article/@type">richtext</item>
        <item path="/create/article/@lang">en-us</item>
        <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>

        <namespace of="create" prefix="ns1:"
                   url="http://predic8.com/wsdl/material/ArticleService/1/"/>
        <namespace of="article" prefix="ns1:"
                   url="xmlns:ns1='http://predic8.com/material/1/"/>
        <namespace of="@lang" prefix="xml:"
                   url="http://www.w3.org/XML/1998/namespace"/>
        <namespace of="price" prefix="ns1:"
                   url="xmlns:ns1='http://predic8.com/material/1/"/>
        <namespace of="id" prefix="ns1:"
                   url="xmlns:ns1='http://predic8.com/material/1/"/>
     </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:variable name="vLocal-name" select=
              "substring-before(concat(current-grouping-key(), '['), '[')"/>

             <xsl:variable name="vNamespace"
                           select="key('kNSFor', $vLocal-name, $vStylesheet)"/>


             <xsl:choose>
              <xsl:when test="starts-with($vLocal-name, '@')">
               <xsl:attribute name=
                 "{$vNamespace/@prefix}{substring($vLocal-name,2)}"
                    namespace="{$vNamespace/@url}">
                 <xsl:value-of select=
                  "substring(
                       substring-after(current-group(), current-grouping-key()),
                       2
                             )"/>
               </xsl:attribute>
              </xsl:when>
              <xsl:otherwise>
               <xsl:element name="{$vNamespace/@prefix}{$vLocal-name}"
                          namespace="{$vNamespace/@url}">

                    <xsl:sequence select=
                     "my:subTree(for $s in current-group()
                                  return
                                     concat('/',substring-after(substring($s, 2),'/'))
                                   )
                     "/>
                 </xsl:element>
              </xsl:otherwise>
             </xsl:choose>
           </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ドキュメント(使用されていない)に適用されると、必要な正しい結果が生成されます。

<ns1:create xmlns:ns1="http://predic8.com/wsdl/material/ArticleService/1/">
   <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/" type="richtext"
                xml:lang="en-us"/>
   <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
      <ns1:id>1</ns1:id>
      <description>bar</description>
      <name>foo</name>
      <ns1:price>
         <amount>00.00</amount>
         <currency>USD</currency>
      </ns1:price>
      <ns1:price>
         <amount>11.11</amount>
         <currency>AUD</currency>
      </ns1:price>
   </ns1:article>
   <ns1:article xmlns:ns1="xmlns:ns1='http://predic8.com/material/1/">
      <ns1:id>2</ns1:id>
      <description>some name</description>
      <name>some description</name>
      <ns1:price>
         <amount>00.01</amount>
         <currency>USD</currency>
      </ns1:price>
   </ns1:article>
</ns1:create>

説明

  1. local-name()生成されたドキュメント全体で、同じ名前空間を持つ2つの要素が同じ名前空間に属しているという合理的な仮定が行われます。これは、実際のXMLドキュメントの大部分をカバーします。

  2. 名前空間の仕様は、パスの仕様に従います。nsmespace仕様の形式は次のとおりです。<namespace of="target element's local-name" prefix="wanted prefix" url="namespace-uri"/>

  3. で要素を生成する前にxsl:element、によって作成されたインデックスを使用して適切な名前空間指定が選択されますxsl:key。この名前空間の指定から、その属性prefixと属性の値は、完全な要素名と要素の名前空間-uriの値を命令urlで指定する際に使用されます。xsl:element

于 2012-07-10T03:44:52.540 に答える
0

興味深い質問です。XPath 式の入力セットがいくつかの妥当な制約を満たしていると仮定しましょう。たとえば、X/article[2] がある場合、(その前に) X/article[1] もあります。そして、問題の名前空間の部分を脇に置きましょう。

XSLT 2.0 ソリューションに進みましょう。フォームへの入力から始めます。

<paths>
<path value="1">/create/article[1]/id</path>
<path value="bar">/create/article[1]/description</path>
</paths>

これを

<paths>
<path value="1"><step>create</step><step>article[1]</step><step>id</step></path>
   ...
</paths>

ここで、最初のステップでグループ化を行う関数を呼び出し、次のステップでグループ化を行うために自身を再帰的に呼び出します。

<xsl:function name="f:group">
  <xsl:param name="paths" as="element(path)*"/>
  <xsl:param name="step" as="xs:integer"/>
  <xsl:for-each-group select="$paths" group-by="step[$step]">
    <xsl:element name="{replace(current-grouping-key(), '\[.*', '')}">
      <xsl:choose>
        <xsl:when test="count(current-group) gt 1">
           <xsl:sequence select="f:group(current-group(), $step+1)"/>
        </xsl:when>
        <xsl:otherwise>
           <xsl:value-of select="current-group()[1]/@value"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:element>
  </xsl:for-each-group>
</xsl:function>

それはテストされておらず、機能させるために調整しなければならない詳細があるかもしれません. しかし、基本的なアプローチはうまくいくはずだと思います。

問題の名前空間部分は、パスのリストを前処理して各ステップ要素に名前空間属性を追加することによって、おそらく最もうまく対処できます。これを xsl:element 命令で使用して、要素を正しい名前空間に配置できます。

于 2012-07-09T16:03:34.660 に答える