使用 XSLT 样式表对一个元素执行多项操作

Perform multiple operations on one element using XSLT stylesheet

我对用于转换 XML 的 XSLT 样式表还很陌生,我需要它来进行更大的 BI 项目。

我有以下 XML 结构:

<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<zylab>
    <document version="1.1" guid="{00A4A300-E76C-4373-A35B-1D2F0FF1336B}" date="20110908" time="08:48:52.436" size="2464" path="D:\ZYIMAGE DATA\INDEX DATA\EMD\TXT11[=10=]000000\" name="00000GFP.TXT" key="">
        <fields>
            <field id="Document_datum">20110830</field>
            <field id="Document_type">value</field>
            ...
        </fields>
    </document>
</zylab>

我想使用 XSLT 将其转换为这种格式

<?xml version="1.0" encoding="UTF-8"?>
<zylab>
    <document version="1.1" guid="{00A4A300-E76C-4373-A35B-1D2F0FF1336B}" date="20110908" time="08:48:52.436" size="2464" path="D:\ZYIMAGE DATA\INDEX DATA\EMD\TXT11[=11=]000000\" name="00000GFP.TXT" key="" />
    <fields Document_datum="20110830" Document_type="value" ... />
</zylab>

到目前为止,我已经能够使用以下 XSLT 完成其中的一部分:

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

    <!--Identity template, provides default behavior that copies all content into the output -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

  <!-- copy node one level up -->
  <xsl:template match="fields">
    <xsl:copy>
        <xsl:apply-templates select="child::node()[not(self::field)]"/>
    </xsl:copy>
    <xsl:apply-templates select="field"/>
  </xsl:template>

  <!-- transpose elements to attributes -->
  <xsl:template match="fields">
    <xsl:element name="fields">
      <xsl:for-each select="*">
        <xsl:attribute name="{@id}">
          <xsl:value-of select="text()"/>
        </xsl:attribute>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

问题是片段确实独立工作,但我无法让它对同一元素执行这两个操作。 输出取决于我的片段的顺序,后者总是适用的。如果我注释掉其中一个,另一个似乎可以正常工作。

这里有人知道如何修改我的 XSLT 以对 fields 元素执行这两个操作,以便将它的 fields 子元素合并到属性并将 fields 元素移动到与文档元素相同的级别吗?

简单的怎么样:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<!-- convert each field to an attribute of parent element (fields)  -->
<xsl:template match="field">
    <xsl:attribute name="{@id}">
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

请注意,这里假定 id 属性的内容适合构成有效的属性名称。


编辑:

如果要将 fields 元素移动为 document 的同级元素,请再添加一个模板:

<!-- move fields to be a sibling of document -->
<xsl:template match="document">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
    </xsl:copy>
    <xsl:apply-templates select="fields"/>
</xsl:template> 

编辑 2:

it seems my BI (Microsoft SSIS) XSLT transformation does not like the strip-space macro: But it seems pretty relevant to the transformation as w/o it it does not work at all. Is there any alternative solution?

很难说不合格的处理器会做什么或不会做什么。尝试再添加一个模板:

<xsl:template match="fields">
    <xsl:copy>
        <xsl:apply-templates select="field"/>
    </xsl:copy>
</xsl:template>

或替换此模板:

<!-- convert each field to an attribute of parent element (fields)  -->
<xsl:template match="field">
    <xsl:attribute name="{@id}">
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

与:

<xsl:template match="fields">
    <xsl:copy>
        <xsl:for-each select="field">
            <xsl:attribute name="{@id}">
                <xsl:value-of select="text()"/>
            </xsl:attribute>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

或者使用以下方法删除仅包含空白的文本节点:

<xsl:template match="text()[not(string)]"/>

最终我结合了我的原始设置和 michael.hor257k 的答案,这是在我的 BI 解决方案 (SSIS) 的外部和内部工作的 XSLT 样式表:

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

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

    <!-- transpose elements to attributes -->
    <xsl:template match="fields">
        <xsl:element name="fields">
            <xsl:for-each select="*">
                <xsl:attribute name="{@id}">
                    <xsl:value-of select="text()"/>
                </xsl:attribute>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>

    <!-- move fields to be a sibling of document -->
    <xsl:template match="document">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
        </xsl:copy>
        <xsl:apply-templates select="fields"/>
    </xsl:template> 

</xsl:stylesheet>