使用xslt在输出xml中插入元素

时间:2011-04-15 21:24:48

标签: xslt

我有以下xml文档:

<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
 </config>

我需要检查名称为“prop3”,“prop4”,“prop5”的属性是否存在,如果不存在,我需要在配置下添加它们,保留现有元素。具有这些属性的元素可能存在,在这种情况下,我需要更改它们的值。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:0)

以下是此类功能的示例xsl样式表:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/">
        <config>
            <xsl:for-each select="config/property">
                <!-- Fills up from 1 until first prop -->
                <xsl:if test="string(number(substring(preceding-sibling::property/@name,5,1) + 1)) = 'NaN' and number(substring(@name,5,1)) &gt; 1">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="1"/>
                        <xsl:with-param name="until" select="number(substring(@name,5,1))"/>
                    </xsl:call-template>
                </xsl:if>
                <!-- Fills up the gaps -->
                <xsl:if test="number(substring(@name,5,1)) &gt; number(substring(preceding-sibling::property/@name,5,1) + 1)">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="number(substring(preceding-sibling::property/@name,5,1) + 1)"/>
                        <xsl:with-param name="until" select="number(substring(@name,5,1))"/>
                    </xsl:call-template>
                </xsl:if>
                <!-- copies the current node -->
                <property>
                    <xsl:attribute name="name">
                        <xsl:value-of select="@name"/>
                    </xsl:attribute>
                    <xsl:value-of select="text()"/>
                </property>
                <!-- Fills up to 5 from last prop -->
                <xsl:if test="substring(following-sibling::property/@name, 1, 4) != 'prop'">
                    <xsl:call-template name="filler">
                        <xsl:with-param name="from" select="number(substring(@name,5,1)) + 1"/>
                        <xsl:with-param name="until" select="6"/>
                    </xsl:call-template>
                </xsl:if>
            </xsl:for-each>
        </config>
    </xsl:template>
    <xsl:template name="filler" match="property">
        <xsl:param name="from"/>
        <xsl:param name="until"/>
        <xsl:choose>
            <xsl:when test="$from &lt; $until">
                <property>
                    <xsl:attribute name="name">
                        <xsl:value-of select="concat('prop', $from)"/>
                    </xsl:attribute>
                    <xsl:value-of select="concat('val ', $from)"/>
                </property>
                <xsl:call-template name="filler">
                    <xsl:with-param name="from" select="$from + 1" />
                    <xsl:with-param name="until" select="$until"/>
                </xsl:call-template>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

这段代码基本上填满了xml,以确保它包含从prop1到prop5的所有值。

不是那么复杂 - 因为我不知道确切的目的 - 但也许它会帮助你,你可以根据自己的需要进行推广。

这样输出将是

<?xml version='1.0' encoding='UTF-8' ?>
<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
  <property name="prop3">val 3</property>
  <property name="prop4">val 4</property>
  <property name="prop5">val 5</property>
</config>

以下任何输入:

<config>
    <property name="prop1">val 1</property>
    <property name="prop2">val 2</property>
</config>

<config>
    <property name="prop2">val 2</property>
</config>

<config>
    <property name="prop1">val 1</property>
    <property name="prop4">val 4</property>
</config>

答案 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:replacements>
    <property name="prop3">Newval 3</property>
    <property name="prop4">Newval 4</property>
    <property name="prop5">Newval 5</property>
 </my:replacements>

 <xsl:variable name="vReps" select=
  "document('')/*/my:replacements/*"/>

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

 <xsl:template match=
  "property
    [@name
    =
     document('')/*/my:replacements/*/@name
    ]
  "/>

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

应用于提供的XML文档时:

<config>
    <property name="prop1">val 1</property>
    <property name="prop2">val 2</property>
</config>

产生完全正确的结果:

<config>
  <property name="prop1">val 1</property>
  <property name="prop2">val 2</property>
  <property name="prop3" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 3</property>
  <property name="prop4" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 4</property>
  <property name="prop5" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">Newval 5</property>
</config>

<强>解释

  1. 身份规则/模板“按原样”复制每个节点。

  2. 具有property属性的任何name元素的覆盖模板具有name元素的my:replacements/property个属性之一的值,在效果中删除任何来自文档的这样的元素(因为模板的空体。

  3. 与文档顶部元素匹配的覆盖模板对其进行浅层复制,然后复制其属性并将模板应用于其所有后代节点(实际上复制除了上面2中的所有节点之外的所有节点。 ,它复制所有替换元素,这些元素是嵌入式my:replacements元素的子元素。

  4. 当这些替换元素位于单独的XML文档中时(在现在仅为方便起见的XSLT样式表中),新复制的替换元素中出现的命名空间节点将消失。

  5. 请注意:此解决方案的主要思想是在元素存在时替换元素或在元素不存在时添加元素 - 这与(不检查存在)复制相同新元素会覆盖任何现有元素。