3

次のようなサンプルxmlファイルがあります。

--- before transformation ---
<root-node>

   <child-type-A> ... </child-type-A>
   <child-type-A> ... </child-type-A>
   <child-type-B> ... </child-type-B>
   <child-type-C>
      <child-type-B> ... </child-type-B>
      ...
   </child-type-C>


   ...

</root-node>

このxmlファイルを次のようなものに変換したいと思います:

--- after transformation ---
<root-node>

   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node>
      <child-node> ... </child-node>
      ...
   </child-node>

   ...

</root-node>

これは事実上、文書構造は同じままですが、「選択された」要素の名前が変更されることを意味します。これらの選択された要素は、同じプレフィックス (この例では "child-type-") で始まりますが、さまざまなサフィックス ("A" | "B" | "C" | など) があります。

なぜこのすべての面倒なのですか?入力として xml ファイルを要求するソフトウェアがあります。便宜上、XML スキーマを使用して xml ファイルを簡単に編集します。スキーマは、xml ファイルが正しいことを確認するのに役立ちます。悲しいことに、XML スキーマには、コンテキスト依存の側面に関してはいくらか欠けています。これにより、xml ファイルは /before conversion/ に示されているようになります。ソフトウェアは、/after conversion/ に示されているようなファイルを想定しているため、このような xml ファイルを処理できません。したがって、変換の必要性。


XSLT を使用して変換を行いたいのですが、その方法は既にわかっています。私のアプローチは、恒等変換のルールを定義し、名前を変更する必要がある「child-type-*」要素ごとに 1 つのルールを定義することでした。このソリューションは機能しますが、それほどエレガントではありません。たくさんのルールができあがります。

--- sample transformation rules ---

<!-- Identity transformation -->
<xsl:template match="@*|node()">
   <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
   </xsl:copy>
</xsl:template>

<xsl:template match="child-type-A">
   <xsl:element name="child-node">
      <xsl:apply-templates select="@*|node()" />
   </xsl:element>
</xsl:template>

...

それを2つのルールだけに凝縮する方法はありますか? 1 つは恒等変換用で、もう 1 つはすべての「child-type-*」要素用ですか? XSLT を何らかの正規表現と組み合わせて使用​​することでしょうか? それとも、そのような問題に取り組むために別のアプローチを取る必要がありますか?

4

4 に答える 4

2

(私の答えを修正しました)

このスニペットは、サンプル XML で問題なく動作します。2 つのテンプレートは両方とも「すべての要素」に作用するため、マージしました。以前のテンプレートは両方とも同じ選択に一致したため、機能しませんでした。

<xsl:template match="@*|node()">
    <xsl:choose>
        <xsl:when test="starts-with(name(), 'child-type')">
            <xsl:element name="child-node">
                <xsl:apply-templates select="@*|node()"/>
            </xsl:element>
        </xsl:when>
        <xsl:otherwise>
           <xsl:copy>
              <xsl:apply-templates select="@*|node()" />
           </xsl:copy>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

ソース XML が次の場合:

<root-node>
   <child-type-A> ... </child-type-A>
   <child-type-A> ... </child-type-A>
   <child-type-B> ... </child-type-B>
   <child-type-C>
      <child-type-B> ... </child-type-B>
   </child-type-C>
</root-node>

これにより、次の出力が得られます。

<root-node>
<child-node> ... </child-node>
<child-node> ... </child-node>
<child-node> ... </child-node>
<child-node>
    <child-node> ... </child-node>
</child-node>
</root-node>
于 2011-05-20T07:44:27.617 に答える
1

要素名の内部構文に意味を付けて情報をキャプチャすることはお勧めできません (極端な場合、すべての情報がルート要素の名前でキャプチャされた XML ドキュメントを持つことができます<Surname_Kay.Firstname_Michael.Country_UK/>)。ただし、その形式のデータがある場合は、たとえば形式のテンプレート ルールを使用して、それを処理することは確かに可能です。<xsl:template match="*[matches(name(), 'child-type-[A-Z]')]">

于 2011-05-20T14:06:11.410 に答える
1

XSLtT にはstarts-with関数があり、これを使用して、で始まる要素を識別し'child-type'、単一のテンプレート マッチを使用できます。この関連する質問を参照してください。

最初の名前に一致する要素を選択します

于 2011-05-20T07:35:43.337 に答える
0

これは、目的の接頭辞と、目的の接頭辞ごとに接尾辞のセットを指定するパラメータで機能する一般的な XSLT 1.0 変換です。これにより、この接頭辞とこれらの接尾辞のいずれかを持つすべての要素名を、目的の新しい名前に変更する必要があります。名前:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="my" >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <my:renames>
  <rename prefix="child-type-"
          newVal="child-node">
    <suffix>A</suffix>
    <suffix>B</suffix>
    <suffix>C</suffix>
  </rename>
 </my:renames>

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

 <xsl:template match="/*//*">
  <xsl:choose>
  <xsl:when test=
   "document('')/*
         /my:renames
           /rename
             [@prefix[starts-with(name(current()),.)]
            and
              suffix
               [substring(name(current()),
                          string-length(name(current()))
                          - string-length(.) +1
                          )
               =
                 .
               ]
              ]
    ">

  <xsl:variable name="vNewName" select=
   "document('')/*
         /my:renames
           /rename
             [@prefix[starts-with(name(current()),.)]
            and
              suffix
               [substring(name(current()),
                          string-length(name(current()))
                          -string-length(.) +1
                          )
               =
                 .
               ]
              ]
              /@newVal
   "/>

      <xsl:element name="{$vNewName}">
       <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
   </xsl:when>
   <xsl:otherwise>
    <xsl:call-template name="identity"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

提供された XML ドキュメントに適用した場合:

<root-node>
    <child-type-A> ... </child-type-A>
    <child-type-A> ... </child-type-A>
    <child-type-B> ... </child-type-B>
    <child-type-C>
      <child-type-B> ... </child-type-B>
      ...
    </child-type-C>
      ...
</root-node>

必要な正しい結果が生成されます。

<root-node>
   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node> ... </child-node>
   <child-node>
      <child-node> ... </child-node>
      ...
    </child-node>
      ...
</root-node>

注意: この変換を使用すると、外部パラメータ/ドキュメントとして指定された異なる接頭辞と関連する接尾辞を持つ異なる要素の名前を同時に変更できます。

Ⅱ.同等の XSLT 2.0 ソリューション:

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

 <xsl:variable name="vRules">
  <rule prefix="^child\-type\-" newVal="child-node">
    <suffix>A$</suffix>
    <suffix>B$</suffix>
    <suffix>C$</suffix>
  </rule>
 </xsl:variable>

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

 <xsl:template match=
  "*[for $n in name(.),
         $r in $vRules/*
                 [matches($n, @prefix)], 
         $s in $vRules/*/suffix
                 [matches($n, .)]
      return $r and $s
    ]">

    <xsl:variable name="vN" select="name()"/>

    <xsl:variable name="vNewName" select=
     "$vRules/*
           [matches($vN, @prefix)
           and 
            suffix[matches($vN, .)]
           ]
           /@newVal
     "/>
   <xsl:element name="{$vNewName}">
    <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
 </xsl:template>
</xsl:stylesheet>

同じ XML ドキュメント (上記) に適用すると、同じ正しい出力が生成されます。

于 2011-05-22T04:10:59.017 に答える