使用 XSLT 将具有相同 ID 的元素 (XML) 合并到 txt 文件

Posted

技术标签:

【中文标题】使用 XSLT 将具有相同 ID 的元素 (XML) 合并到 txt 文件【英文标题】:Merge Elements (XML) With Same ID to txt file using XSLT 【发布时间】:2021-12-30 00:16:30 【问题描述】:

我有一个任务,我需要遍历 XML 文档并合并具有相同 ID 的元素/节点。输出应该是一个 csv 文件(用于进一步处理),其中每一行都有固定的长度。根据节点的值,该值需要放置在输出中的某个位置。

这是一个XML示例

<root>
    <User>
        <UserID>55555</UserID>
        <Value>Active</Value>
    </User>
    <User>
        <UserID>55555</UserID>
        <Value>Admin</Value>
    </User>
    <User>
        <UserID>55555</UserID>
        <Value>Eligible</Value>
    </User>
    <User>
        <UserID>123456</UserID>
        <Value>Active</Value>
    </User>
</root>

我的想要的输出是:

User ID, Active, Admin, Eligible
55555, Y, Y, Y,
123456, Y, N, N,

请注意,这些值始终相同(活动、管理员和合格),但用户可以拥有不同数量的值,如示例中所示。

目前这是我得到的:

    <xsl:template match="/root">
        <Header>
            <xsl:text>User ID</xsl:text>
            <xsl:value-of select="$comma"/>
            
            <xsl:text>Active</xsl:text>
            <xsl:value-of select="$comma"/>
            
            <xsl:text>Admin</xsl:text>
            <xsl:value-of select="$comma"/>
            
            <xsl:text>Eligible</xsl:text>
            <xsl:text>&#xa;</xsl:text>
        </Header>
            <xsl:for-each-group select="User" group-by="UserID"> 
                
                <!-- User ID -->
                <xsl:value-of select="UserID"/>
                <xsl:value-of select="$comma"/>
                
                <xsl:for-each-group select="current-group()" group-by="Value">
                    <xsl:value-of select="current-grouping-key()"/>
                    <xsl:value-of select="$comma"/>
                </xsl:for-each-group>
                
                <xsl:value-of select="$lineFeed"/>
            </xsl:for-each-group>
    </xsl:template>

该组并选择正确的元素,但随后我需要将它们放在正确的标题下(例如具有所需输出的 ​​axample)。

任何人都可以在这里指出正确的方向吗?任何帮助将不胜感激。

【问题讨论】:

这很难理解。您是在问“詹姆斯”和“纽约”只是相应列中有值的指标吗?如果是这样,姓氏列的指示符是什么?此外,您说“输出应该是一个 csv 文件”,但是您说“每行都有固定长度”。而且您的样式表似乎正在生成一个制表符分隔的文件(带有 XML 标头!)。这些是三(或四)种不同的东西。 这只是示例。使用“姓名”“姓氏”和“城市”来显示它并不是最好的方式。这个想法是应该在这些标题下的字段始终具有相同的值。因此,如果 James 那么这应该在 Name 等下。“Lastname”的指示符将是 Peterson。正如您所指出的,我更改了变量的名称以反映逗号而不是错误的制表符。 【参考方案1】:

如果我理解正确(如果非常大!),你想做这样的事情:

XSLT 2.0

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

<xsl:template match="/root">
    <!-- header -->
    <xsl:text>User ID, Active, Admin, Eligible&#10;</xsl:text>
    <!-- rows -->
    <xsl:for-each-group select="User" group-by="UserID"> 
        <!-- User ID -->
        <xsl:value-of select="UserID"/>
        <xsl:text>, </xsl:text>
        <!-- Active -->
        <xsl:value-of select="if (current-group()/Value[.='Active']) then 'Y' else'N'"/>
        <xsl:text>, </xsl:text>
        <!-- Admin -->
        <xsl:value-of select="if (current-group()/Value[.='Admin']) then 'Y' else'N'"/>
        <xsl:text>, </xsl:text>
        <!-- Eligible -->
        <xsl:value-of select="if (current-group()/Value[.='Eligible']) then 'Y' else'N'"/>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

或者更简洁:

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

<xsl:template match="/root">
    <!-- header -->
    <xsl:text>User ID, Active, Admin, Eligible&#10;</xsl:text>
    <!-- rows -->
    <xsl:for-each-group select="User" group-by="UserID"> 
        <xsl:value-of select="UserID, for $t in ('Active', 'Admin', 'Eligible') return if (current-group()/Value[.=$t]) then 'Y' else 'N'" separator=", "/>
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

请注意,结果与您发布的结果略有不同:每条记录中没有尾随逗号。

【讨论】:

感谢迈克尔的回复。这正是我一直在寻找的!我看到您注意到我编辑了问题以更清晰,并且您调整了答案。

以上是关于使用 XSLT 将具有相同 ID 的元素 (XML) 合并到 txt 文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 XSLT 组合具有相同 ID 及其值的节点 (XML)

使用 xslt 2.0 合并具有相同父属性值的元素的 xml 内容

如何获取具有相同名称的元素并根据 XSLT 中的子节点值对它们进行排序

xslt将具有相同属性的相邻兄弟合并为一个,同时连接它们的文本

使用 XSLT 程序对具有逗号分隔值的 XML 元素进行分组

具有相同级别下重复节点的 XSLT 变换