将XML元素替换为该元素的多个副本

时间:2014-03-05 17:22:44

标签: xml xslt

我有一个包含

等元素的XML文件
  <function name="negate" arg1="multivector"/>

我想转换这些元素,以便{8}被multivectorvectorbivector等所有内容替换为rotor。所以转换后的XML将会有8个negate元素:

  <function name="negate" arg1="multivector"/>
  <function name="negate" arg1="multivector3"/>
  <function name="negate" arg1="vector"/>
  <function name="negate" arg1="vector3"/>
  <function name="negate" arg1="vector"/>
  <function name="negate" arg1="bivector3"/>
  <function name="negate" arg1="rotor"/>
  <function name="negate" arg1="rotor3"/>

同样,我的元素有两种arg

  <function name="add" arg1="multivector" arg2="multivector"/>

转换后的XML应该有64个add元素,其中args是vector等的各种组合,尽管我会免除我们明确写出这些内容的痛苦。

我可以弄清楚如何将multivector转换为我的新东西中的任何一个,但我无法弄清楚如何让它多次复制相同的东西。我是否必须手动进行丑陋的更换?我可以优雅地自动循环所有8或64件事物吗?


我的第一个基本努力

以下是我到目前为止所做的,但它只是用arg2="multivector"替换每个arg2="vector"属性的最基本的事情。一般的样式改进也很受欢迎,因为我是XML的新手。

<?xml version="1.0" encoding="UTF-8"?>

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- Find any arg2="multivector" attribute and replace -->
  <xsl:template match="function[@arg2='multivector']/@arg2">
    <xsl:attribute name="arg2">vector</xsl:attribute>
  </xsl:template>

  <!-- Just copy anything that didn't match above -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:transform>

4 个答案:

答案 0 :(得分:1)

我假设你有一些像这样的输入数据:

<functions>
    <function name="xxxxx" arg1="yyyyy" />
    <function name="negate" arg1="multivector"/>
    <function name="negate" arg1="zzzzz" />
    <function name="add" arg1="multivector" arg2="multivector"/>
    <function name="add" arg1="aaaaa" arg2="bbbbb" />
    <function name="add" arg1="ccccc" arg2="ddddd" />
</functions>

并且您希望生成一个新的XML,保留所有现有元素,但具有以下格式的元素除外:

<function name="negate" arg1="multivector"/>

<function name="add" arg1="multivector" arg2="multivector"/>

您希望通过 n <function name="negate" arg1="..."/>元素和n*n <function name="add" arg1="..." arg2="..."/>替换它们,同时保留<function name="xxxxx" arg1="yyyyy" />和{{1}等元素他们在哪里。

您可以将要用于args的数据放在一个单独的文件中(您也可以将它放在XSL文件中,为它定义一个本地命名空间)。我将使用一个包含三个元素:

<function name="negate" arg1="zzzzz" />

我会将此文件称为<?xml version="1.0" encoding="UTF-8"?> <vectors> <vector>vector</vector> <vector>bivector</vector> <vector>rotor</vector> </vectors>

然后将该文件读入XSLT样式表,将vectors.xml节点集存储在变量中,循环遍历每个元素两次并生成所有组合。您还将输入文档函数存储在变量中,以便复制不受影响的节点。

这是样式表:

vector

使用包含上面列出的数据作为输入的<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:variable name="vectors" select="document('vectors.xml')/vectors"/> <xsl:variable name="functions" select="/functions"/> <xsl:template match="functions"> <xsl:copy> <xsl:apply-templates select="$vectors/vector"/> </xsl:copy> </xsl:template> <xsl:template match="vector"> <xsl:variable name="arg1" select="."/> <xsl:for-each select="$functions/function"> <xsl:choose> <xsl:when test="@name='negate' and @arg1='multivector'"> <function name="negate" arg1="{$arg1}"/> </xsl:when> <xsl:when test="@name='add' and @arg1='multivector' and @arg2='multivector'"> <xsl:for-each select="$vectors/vector"> <function name="add" arg1="{$arg1}" arg2="{.}"/> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:copy-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:template> </xsl:stylesheet> 文件,并将上面列出的functions文件放在同一目录中,它将生成:

vectors.xml

答案 1 :(得分:1)

有趣的问题!

XSLT 1.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:output indent="yes"/>

  <!-- The terminating space in this list is required for being able
       to use the "chopping off" method substring-before(..., ' ') 
       for all values, including the last one. -->
  <xsl:variable name="argValues" 
    select="'multivector multivector3 vector vector3 vector bivector3 rotor rotor3 '"/>

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

  <xsl:template match="function[@arg1='multivector']" priority="1">
    <!-- We solve the problem recursively: 
         This template applies itself multiple times.

         We start with a complete space separated list of argValues 
         and chop off the first value in arg1Values in each iteration.
         We only chop off the first value in arg2Values if we have 
         used up all values in arg1Values.
         In this case, we also "re-fill" the arg1Values list.

         Initially, the apply-templates in the identity template sends us here. 
         It does not supply any parameters, so we "initialize" them with the
         complete list of argValues here. -->
    <xsl:param name="arg1Values" select="$argValues"/>
    <xsl:param name="arg2Values" select="$argValues"/>

    <!-- Here, the "chopping off" takes place. -->
    <xsl:variable name="currentArg1Value" select="substring-before($arg1Values,' ')"/>
    <xsl:variable name="remainingArg1Values" select="substring-after($arg1Values,' ')"/>
    <!-- We also chop off the first value from arg2Values,
         but we only supply the chopped off list to the recursion
         if all values from arg1Values have been used up. 
         Otherwise, we continue using the current state of arg2Values. -->
    <xsl:variable name="currentArg2Value" select="substring-before($arg2Values,' ')"/>
    <xsl:variable name="remainingArg2Values" select="substring-after($arg2Values,' ')"/>

    <xsl:choose>
      <xsl:when test="$currentArg1Value != ''">
        <!-- If there still are arg1 values in the list,
             we create a function element with  -->
        <function name="{@name}" arg1="{$currentArg1Value}">
          <!-- We only have to add an @arg2 attribute if the
               matched function element already has one. -->
          <xsl:if test="@arg2">
            <xsl:attribute name="arg2">
              <xsl:value-of select="$currentArg2Value"/>
            </xsl:attribute>
          </xsl:if>
        </function>
        <!-- We re-apply the template with the first value
             chopped off from the list of arg1 values. -->
        <xsl:apply-templates select=".">
          <xsl:with-param name="arg1Values" select="$remainingArg1Values"/>
          <xsl:with-param name="arg2Values" select="$arg2Values"/>
        </xsl:apply-templates>
      </xsl:when>

      <xsl:when test="@arg2 and $remainingArg2Values != ''">
        <!-- If there are no more arg1 values in the list, 
             we go on with the next arg2 value.
             We're not supplying arg1Values so that the default 
             (the full list) takes effect. -->
        <xsl:apply-templates select=".">
          <xsl:with-param name="arg2Values" select="$remainingArg2Values"/>
        </xsl:apply-templates>
      </xsl:when>

      <!-- If we no <when> case applies, all lists have been used up
           and we've output all needed argument combinations.
           The recursion terminates. -->
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

XSLT 2.0

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all">

  <xsl:output indent="yes"/>

  <xsl:variable name="argValues" select="(
      'multivector','multivector3','vector','vector3','vector','bivector3','rotor','rotor3'
     )" as="xs:string*"/>

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

  <xsl:template match="function[@arg1='multivector']" priority="1">
    <!-- We do two nested iterations over the list of argValues.
         During those iterations, we'll lose the matched function element
         as our "current node", therefore we save it to a variable. -->
    <xsl:variable name="functionElement" select="." as="element()"/>

    <!-- If we the matched function alement has an @arg2 attribute,
         we iterate over the full list of argument values.
         If we don't have @arg2, we use the dummy string 'noArg2'
         so that we have something to "iterate over". -->
    <xsl:for-each select="if (@arg2) then $argValues else 'noArg2'">
      <!-- Like the functionElement, we save the arg2 value to a variable
           so that we can enter the next for-each. -->
      <xsl:variable name="arg2" select="." as="xs:string"/>

      <xsl:for-each select="$argValues">
        <function name="{$functionElement/@name}" arg1="{.}">
          <!-- We only need to add @arg2 if the matched
               function element already has one. -->
          <xsl:if test="$functionElement/@arg2">
            <xsl:attribute name="arg2">
              <xsl:value-of select="$arg2"/>
            </xsl:attribute>
          </xsl:if>
        </function>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

<强>输入

<?xml version="1.0" encoding="UTF-8"?>
<test>
  <function name="negate" arg1="multivector"/>
  <function name="add" arg1="multivector" arg2="multivector"/>
  <function name="add" arg1="otherArg" arg2="multivector"/>
  <function name="noArgs"/>  
</test>

<强>输出

<?xml version="1.0" encoding="UTF-8"?>
<test>
  <function name="negate" arg1="multivector"/>
  <function name="negate" arg1="multivector3"/>
  <function name="negate" arg1="vector"/>
  <function name="negate" arg1="vector3"/>
  <function name="negate" arg1="vector"/>
  <function name="negate" arg1="bivector3"/>
  <function name="negate" arg1="rotor"/>
  <function name="negate" arg1="rotor3"/>
  <function name="add" arg1="multivector" arg2="multivector"/>
  <function name="add" arg1="multivector3" arg2="multivector"/>
  <function name="add" arg1="vector" arg2="multivector"/>
  <function name="add" arg1="vector3" arg2="multivector"/>
  <function name="add" arg1="vector" arg2="multivector"/>
  <function name="add" arg1="bivector3" arg2="multivector"/>
  <function name="add" arg1="rotor" arg2="multivector"/>
  <function name="add" arg1="rotor3" arg2="multivector"/>
  <function name="add" arg1="multivector" arg2="multivector3"/>
  <function name="add" arg1="multivector3" arg2="multivector3"/>
  <function name="add" arg1="vector" arg2="multivector3"/>
  <function name="add" arg1="vector3" arg2="multivector3"/>
  <function name="add" arg1="vector" arg2="multivector3"/>
  <function name="add" arg1="bivector3" arg2="multivector3"/>
  <function name="add" arg1="rotor" arg2="multivector3"/>
  <function name="add" arg1="rotor3" arg2="multivector3"/>
  <function name="add" arg1="multivector" arg2="vector"/>
  <function name="add" arg1="multivector3" arg2="vector"/>
  <function name="add" arg1="vector" arg2="vector"/>
  <function name="add" arg1="vector3" arg2="vector"/>
  <function name="add" arg1="vector" arg2="vector"/>
  <function name="add" arg1="bivector3" arg2="vector"/>
  <function name="add" arg1="rotor" arg2="vector"/>
  <function name="add" arg1="rotor3" arg2="vector"/>
  <function name="add" arg1="multivector" arg2="vector3"/>
  <function name="add" arg1="multivector3" arg2="vector3"/>
  <function name="add" arg1="vector" arg2="vector3"/>
  <function name="add" arg1="vector3" arg2="vector3"/>
  <function name="add" arg1="vector" arg2="vector3"/>
  <function name="add" arg1="bivector3" arg2="vector3"/>
  <function name="add" arg1="rotor" arg2="vector3"/>
  <function name="add" arg1="rotor3" arg2="vector3"/>
  <function name="add" arg1="multivector" arg2="vector"/>
  <function name="add" arg1="multivector3" arg2="vector"/>
  <function name="add" arg1="vector" arg2="vector"/>
  <function name="add" arg1="vector3" arg2="vector"/>
  <function name="add" arg1="vector" arg2="vector"/>
  <function name="add" arg1="bivector3" arg2="vector"/>
  <function name="add" arg1="rotor" arg2="vector"/>
  <function name="add" arg1="rotor3" arg2="vector"/>
  <function name="add" arg1="multivector" arg2="bivector3"/>
  <function name="add" arg1="multivector3" arg2="bivector3"/>
  <function name="add" arg1="vector" arg2="bivector3"/>
  <function name="add" arg1="vector3" arg2="bivector3"/>
  <function name="add" arg1="vector" arg2="bivector3"/>
  <function name="add" arg1="bivector3" arg2="bivector3"/>
  <function name="add" arg1="rotor" arg2="bivector3"/>
  <function name="add" arg1="rotor3" arg2="bivector3"/>
  <function name="add" arg1="multivector" arg2="rotor"/>
  <function name="add" arg1="multivector3" arg2="rotor"/>
  <function name="add" arg1="vector" arg2="rotor"/>
  <function name="add" arg1="vector3" arg2="rotor"/>
  <function name="add" arg1="vector" arg2="rotor"/>
  <function name="add" arg1="bivector3" arg2="rotor"/>
  <function name="add" arg1="rotor" arg2="rotor"/>
  <function name="add" arg1="rotor3" arg2="rotor"/>
  <function name="add" arg1="multivector" arg2="rotor3"/>
  <function name="add" arg1="multivector3" arg2="rotor3"/>
  <function name="add" arg1="vector" arg2="rotor3"/>
  <function name="add" arg1="vector3" arg2="rotor3"/>
  <function name="add" arg1="vector" arg2="rotor3"/>
  <function name="add" arg1="bivector3" arg2="rotor3"/>
  <function name="add" arg1="rotor" arg2="rotor3"/>
  <function name="add" arg1="rotor3" arg2="rotor3"/>
  <function name="add" arg1="otherArg" arg2="multivector"/>
  <function name="noArgs"/>  
</test>

答案 2 :(得分:0)

这是一个适用于XSLT 2.0的解决方案,如果你疯狂到可以尝试,它甚至可以扩展到支持 arg3 ......

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output method="xml" indent="yes"/>

   <xsl:variable name="vectors" select="document('vectors.xml')/vectors"/>

   <xsl:template match="function[@arg1='multivector']">
      <xsl:apply-templates select="$vectors/vector">
         <xsl:with-param name="function" select="."/>
      </xsl:apply-templates>
   </xsl:template>

   <xsl:template match="vectors"/>

   <xsl:template match="vector">
      <xsl:param name="function"/>
      <xsl:param name="arg" select="1"/>
      <xsl:param name="attrs"/>
      <xsl:choose>
         <xsl:when test="$function/@*[name()=concat('arg', (number($arg) + 1))]">
            <xsl:apply-templates select="$vectors/vector">
               <xsl:with-param name="function" select="$function"/>
               <xsl:with-param name="arg" select="number($arg) + 1"/>
               <xsl:with-param name="attrs">
                  <xsl:copy-of select="$attrs"/>
                  <attribute name="arg{$arg}">
                     <xsl:value-of select="."/>
                  </attribute>
               </xsl:with-param>
            </xsl:apply-templates>
         </xsl:when>
         <xsl:otherwise>
            <function name="{$function/@name}">
               <xsl:for-each select="$attrs/attribute">
                  <xsl:attribute name="{@name}">
                     <xsl:value-of select="."/>
                  </xsl:attribute>
               </xsl:for-each>
               <xsl:attribute name="arg{$arg}">
                  <xsl:value-of select="."/>
               </xsl:attribute>
            </function>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

这假设您有一个名为 vectors.xml 的单独文件,其中包含以下XML

<vectors>
    <vector>vector</vector>
    <vector>multivector</vector>
    <vector>multivector3</vector>
    <vector>vector</vector>
    <vector>vector3</vector>
    <vector>vector</vector>
    <vector>bivector3</vector>
    <vector>rotor</vector>
    <vector>rotor3</vector>
</vectors>

答案 3 :(得分:0)

其他答案都没有对我有用。但我确实弄清楚如何使用托马斯的答案基本上做一个for循环。我最终使用了一些或多或少的模板,如下所示。通过调整模板的优先级并匹配不同的东西,我设法得到了我需要的结果。 [出于某种原因,我还需要在大多数功能之后添加换行符&#10;

<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:variable name="smvTypes"
                select="'mv', 'mv3', 'vector',
                        'vector3', 'bivector', 'bivector3',
                        'pseudovector', 'spinor', 'spinor3'"/>

  <xsl:template match="function[@arg1='anysmv']" priority="2">
    <xsl:variable name="functionElement" select="."/>
    <xsl:for-each select="1 to count($smvTypes)">
      <function>
        <xsl:copy-of select="$functionElement/@*"/> <!-- Copy all attributes -->
        <xsl:attribute name="arg1"> <!-- Reset my attribute -->
          <xsl:value-of select="subsequence($smvTypes, ., 1)"/>
        </xsl:attribute>
      </function>
      <xsl:if test=". &lt; count($smvTypes)">
        <xsl:text>&#10;  </xsl:text>
      </xsl:if>
    </xsl:for-each>
  </xsl:template>
</xsl:transform>