来自另一个节点的 XSLT select 属性

XSLT select attributes from another node

这是我的 XML 文件:

<root>
    <headers>
        <header value="type1" />
        <header value="type3" />
        <header value="type2" />
    </headers>
    <data type1="data1_1" type2="data1_2" type3="data1_3" />
    <data type1="data2_1" type2="data2_2" type3="data2_3" />
</root>

而且我想生成 CSV 文件,只包含 headers 部分中列出的 headers。这是我的 xslt 文件:

<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" encoding="unicode" />
<xsl:template match="root">
     <xsl:apply-templates/>
</xsl:template>


<xsl:template match="headers">
    <xsl:apply-templates/><xsl:text>&#xa;</xsl:text>
</xsl:template>


<xsl:template name="separator">
    <xsl:text>&#x09;</xsl:text>
</xsl:template>

<!-- display first line header work ok -->
<xsl:template match="header">
    <xsl:value-of select="@value" />
    <xsl:if test="position() != last()">
      <xsl:call-template name="separator" />
   </xsl:if>
</xsl:template>


<xsl:key name="mykey" match="header" use="@value" />

<!-- For each data parameters check if exist in header and display with the good order ! NOT WORK -->
<xsl:template match="data">
    <xsl:for-each select="@*">
        <xsl:if test="key('mykey', name())">
            <xsl:value-of select="." />
            <xsl:if test="position() != last()">
                <xsl:call-template name="separator" />
            </xsl:if>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

我想要这种输出格式 (CSV):

type1 type3 type2
data1_1 data1_3 data1_2
data2_1 data2_3 data2_2

但我的输出是(data type2 is not under correct header):

type1 type3 type2
data1_1 data1_2 data1_3
data2_1 data2_2 data2_3
  1. 为 text() 节点创建模板以去除元素之间的所有文本(换行、缩进)
  2. 明确地在行之间换行

这是您的 xslt,其中包含您想要的小修复:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:output method="text" indent="no" encoding="unicode" />
    
    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>
            
    <xsl:template match="headers">
        <xsl:apply-templates/><xsl:text>&#xa;</xsl:text>
    </xsl:template>
    
    <xsl:template match="text()"></xsl:template>
    
    <xsl:template name="separator">
        <xsl:text> </xsl:text>
    </xsl:template>
    
    <!-- display first line header work ok -->
    <xsl:template match="header">
        <xsl:value-of select="@value" />
        <xsl:if test="position() != last()">
            <xsl:call-template name="separator" />
        </xsl:if>
    </xsl:template>
    
    
    <xsl:key name="mykey" match="header" use="@value" />
    
    <!-- For each data parameters check if exist in header and display with the good order ! NOT WORK -->
    <xsl:template match="data">
        <xsl:for-each select="@*">
            <xsl:if test="key('mykey', name())">
                <xsl:value-of select="." />
                <xsl:choose>
                    <xsl:when test="position() != last()">
                        <xsl:call-template name="separator" />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:text>
</xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
                
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
    
</xsl:stylesheet>

我会简单地做:

XSLT 1.0

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

<xsl:template match="/root">
    <xsl:variable name="headers" select="headers/header" />
    <!-- header -->
    <xsl:for-each select="$headers">
        <xsl:value-of select="@value"/>
        <xsl:if test="position()!=last()">
            <xsl:text>&#9;</xsl:text>
        </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    <!-- data -->
    <xsl:for-each select="data">
        <xsl:variable name="data" select="." />
        <xsl:for-each select="$headers">
            <xsl:value-of select="$data/@*[name()=current()/@value]"/>
            <xsl:if test="position()!=last()">
                <xsl:text>&#9;</xsl:text>
            </xsl:if>
        </xsl:for-each> 
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

或者(也许更有效),你可以这样做:

XSLT 1.0

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

<xsl:key name="data-value" match="data/@*" use="concat(name(), '|', generate-id(..))" />

<xsl:template match="/root">
    <xsl:variable name="headers" select="headers/header" />
    <!-- header -->
    <xsl:for-each select="$headers">
        <xsl:value-of select="@value"/>
        <xsl:if test="position()!=last()">
            <xsl:text>&#9;</xsl:text>
        </xsl:if>
    </xsl:for-each>
    <xsl:text>&#10;</xsl:text>
    <!-- data -->
    <xsl:for-each select="data">
        <xsl:variable name="data-id" select="generate-id()" />
        <xsl:for-each select="$headers">
            <xsl:value-of select="key('data-value', concat(@value, '|', $data-id))"/>
            <xsl:if test="position()!=last()">
                <xsl:text>&#9;</xsl:text>
            </xsl:if>
        </xsl:for-each> 
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

顺便说一句,结果是 tab-separated 文件,而不是 CSV。

属性没有固定顺序。元素做。因此,让我们使用这些元素来提供您的排序:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" encoding="unicode" />
<xsl:template match="Results">
     <xsl:apply-templates/>
</xsl:template>

<xsl:template match="headers">
    <xsl:apply-templates/><xsl:text>&#xa;</xsl:text>
</xsl:template>

<xsl:template name="separator">
    <xsl:text>&#x09;</xsl:text>
</xsl:template>

<!-- display first line header work ok -->
<xsl:template match="header">
    <xsl:value-of select="@value" />
    <xsl:if test="position() != last()">
      <xsl:call-template name="separator" />
   </xsl:if>
</xsl:template>

<!-- Use the order of the headers to choose the order of the attributes. -->
<xsl:template match="data">
    <xsl:variable name="data" select="."/>
    <xsl:for-each select="/root/headers/header">
        <xsl:value-of select="$data/@*[name()=current()/@value]"/>        
            <xsl:if test="position() != last()">
                <xsl:call-template name="separator" />
            </xsl:if>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

输出:

    type1   
    type3   
    type2   


data1_1 data1_3 data1_2
data2_1 data2_3 data2_2

headers 在不同的行中,因为我没有更改您的那部分代码。