除了某些属性或节点外,如何将XML结构复制到另一个XML结构中

时间:2010-12-01 19:32:56

标签: xml xslt

使用以下XML结构:

<root foo1="bar1" foo2="bar2" foo3="bar3">
    <foo1 foo1="bar1" />

    <data>
        <foo1>bar1</foo1>
        <foo2>bar2</foo2>
        <foo3>bar3</foo3>
    </data>
</root>

我想将此XML结构复制到另一个XML结构中,但在属性和/或node()名称上有一些例外,并获取以下内容 结果使用XSLT 1.0:

<root foo1="bar1" foo2="bar2">    
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

我的规则是:

1)复制除foo3之外的所有根属性

2)复制每个孩子nodes(),除非名为foo1和foo2

我的实际XSL样式表。我设法让root属性限制工作:

<xsl:template match="root">
    <root>
        <xsl:for-each select="./@*">
            <xsl:variable name="name" select="name()" />

            <xsl:if test="name() != 'foo3'">
                <xsl:attribute name="{$name}"><xsl:value-of select="." /></xsl:attribute>
            </xsl:if>
        </xsl:for-each>


    </root>
</xsl:template>

另外,一个更难的问题是: 如果我想动态匹配我的属性和节点怎么办?我想指定服务器端是什么 属性和nodes()我想删除。它可能就像生成一个字符串,然后在<xsl:if>中使用。我不知道这是否可能。

谢谢。

3 个答案:

答案 0 :(得分:1)

您可以使用XPath简化您的选择:

<xsl:for-each select="./@*[not(name()='foo3')]">

然后您不必测试名称。你可以为元素做类似的事情:

*[not(name()='foo2')]

答案 1 :(得分:1)

  

我想复制这个XML   用一些结构变成另一个   例外[...]

     

我的规则是:

     

1)复制除了以外的每个根属性   foo3

     

2)复制每个子节点()除非   那些名为foo1和foo2

从评论中更新

  

嗨,这差不多了。除了那个   应复制data / foo1

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="root/@foo3|root/foo1|foo2"/>
</xsl:stylesheet>

输出:

<root foo1="bar1" foo2="bar2">
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

注意:使用空模板覆盖身份规则

使用参数中的节点名称,此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pStrip" select="'root/@foo3|root/foo1|foo2'"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:param name="pStripPaths" select="concat($pStrip,'|')"/>
        <xsl:param name="pNodePath">
            <xsl:call-template name="path"/>
            <xsl:text>|</xsl:text>
        </xsl:param>
        <xsl:variable name="vStripPath"
                      select="substring-before($pStripPaths,'|')"/>
        <xsl:choose>
            <xsl:when test="not($pStripPaths)">
                <xsl:copy>
                    <xsl:apply-templates select="node()|@*"/>
                </xsl:copy>
            </xsl:when>
            <xsl:when test="contains($pNodePath,concat('/',$vStripPath,'|'))"/>
            <xsl:otherwise>
                <xsl:call-template name="identity">
                    <xsl:with-param name="pStripPaths"
                                    select="substring-after($pStripPaths,'|')"/>
                    <xsl:with-param name="pNodePath" select="$pNodePath"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="node()" name="path" mode="path">
        <xsl:apply-templates select="parent::*" mode="path"/>
        <xsl:value-of select="concat('/',
                                     substring('@',
                                               1 div (count(.|../@*)
                                                      = count(../@*))),
                                     name())"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<root foo1="bar1" foo2="bar2">
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

注意:在XML中,元素名称是指模式,主要定义了层次结构,但不是你的情况。

编辑:只是为了好玩,一个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"
     xmlns:local="http://localhost">
    <xsl:param name="pStrip" select="'root/@foo3|root/foo1|foo2'"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[local:match($pStrip,.)]|@*[local:match($pStrip,.)]"/>
    <xsl:function name="local:match" as="xs:boolean">
        <xsl:param name="pStripPaths" as="xs:string"/>
        <xsl:param name="pNode" as="item()"/>
        <xsl:variable name="vNodePath"
                      select="string-join(($pNode
                                            /ancestor::node()
                                             /name(),
                                           if ($pNode instance of attribute())
                                           then concat('@',name($pNode))
                                           else name($pNode)),
                                           '/')"/>
        <xsl:sequence select="some $path in tokenize($pStripPaths,'\|')
                                  satisfies ends-with($vNodePath,
                                                      concat('/',$path))"/>
    </xsl:function>
</xsl:stylesheet>

编辑2 :所有样式表都遵循相同的字符串模式。

答案 2 :(得分:0)

此转换接受带有XPath表达式的参数到应删除的所有元素/属性

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pDeletes">
  <element>/root/data/foo2</element>
  <element>/root/foo1</element>
  <attribute>/root/@foo3</attribute>
 </xsl:param>

 <xsl:variable name="vDeletes"
  select="msxsl:node-set($pDeletes)"/>

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

 <xsl:template match="*">
  <xsl:variable name="vPath">
   <xsl:apply-templates select="." mode="buildPath"/>
  </xsl:variable>

  <xsl:if test="not(string($vPath) = $vDeletes/element)">
    <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:variable name="vPath">
   <xsl:apply-templates select="." mode="buildPath"/>
  </xsl:variable>

  <xsl:if test="not(string($vPath) = $vDeletes/attribute)">
    <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>

 <xsl:template match="*" mode="buildPath">
  <xsl:for-each select="ancestor-or-self::*">
    <xsl:value-of select="concat('/',name())"/>

    <xsl:variable name="vprecSiblings"
     select="count(preceding-sibling::*[name()=name(current())])"/>

    <xsl:if test="$vprecSiblings">
     <xsl:value-of select="concat('[', $vprecSiblings, ']')"/>
    </xsl:if>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="@*" mode="buildPath">
  <xsl:variable name="vParentPath">
    <xsl:apply-templates select=".." mode="buildPath"/>
  </xsl:variable>

  <xsl:value-of select="concat($vParentPath, '/@', name())"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<root foo1="bar1" foo2="bar2" foo3="bar3">
    <foo1 foo1="bar1" />
    <data>
        <foo1>bar1</foo1>
        <foo2>bar2</foo2>
        <foo3>bar3</foo3>
    </data>
</root>

产生了想要的正确结果

<root foo1="bar1" foo2="bar2">
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

请注意

  1. 标识要删除的节点的每个XPath表达式是全局外部提供的参数elementattribute子项的字符串值。任何此类表达式的位置步骤都包含元素的名称,并且在谓词部分中包含所有相同名称的兄弟节点中的序号。如果这是第一个元素,谓词不应该被强化。对于属性,最后一个位置步骤恰好包含"@"AttributeName,其中AttributeName是属性的名称。

  2. xxx:node-set()扩展功能仅适用于演示用途。实际上,$pDeletes参数将在外部提供,无需在其上应用xxx:node-set()功能。

  3. 所有XPath表达式都可以在一个字符串中提供(使用比| XPath联合运算符更好的分隔符:)),但这可能不如在每个表达式中使用单独的元素。在后一种情况下,可以单独搜索元素或属性,并且如果效率很重要,则可以通过元素或属性名称对表达式进行索引,从而提供非常快速的次线性搜索时间。