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

时间:2015-01-28 07:39:15

标签: xml xslt

我是XSLT样式表的新手,用于转换XML,我需要这个用于更大的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\TXT\2011\36\00000000\" 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\TXT\2011\36\00000000\" 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元素移动到与文档元素相同的文件中吗?

2 个答案:

答案 0 :(得分:1)

简单地说:

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:

  似乎我的BI(微软SSIS)XSLT转换不喜欢   strip-space macro:但它似乎与转换非常相关   因为没有它根本不起作用。有没有替代方案?

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

<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)]"/>

答案 1 :(得分:0)

最后,我结合了原始设置和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>