如何用XSLT处理多元素XML并转换为指定格式CSV?
<d> Elements Great question! XSLT is absolutely a solid fit for this XML-to-CSV transformation—your existing code is almost there, it just misses handling the case where a <b> element has no <d> children. Let's fix that and make it match your desired output exactly.
The Problem with Your Current Stylesheet
Your current code uses <xsl:for-each select="d"> inside the match-b template, which means it only generates output lines when <d> elements exist. When a <b> (like the "third" one in your example) has no <d>s, this loop never runs, so that row gets omitted entirely.
Fixed XSLT Stylesheet
Here's the updated stylesheet that handles both scenarios (with and without <d> elements):
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="text" encoding="iso-8859-1" /> <xsl:strip-space elements="*" /> <xsl:template match="a"> <xsl:for-each select="b"> <xsl:call-template name="process-b"/> </xsl:for-each> </xsl:template> <xsl:template name="process-b"> <!-- First handle cases where there are <d> elements --> <xsl:for-each select="d"> <xsl:value-of select="../@name"/> <xsl:text>,</xsl:text> <xsl:value-of select="../c/@value"/> <xsl:text>,</xsl:text> <xsl:value-of select="../c"/> <xsl:text>,</xsl:text> <xsl:value-of select="."/> <xsl:text>,</xsl:text> <xsl:value-of select="../e"/> <xsl:text> </xsl:text> </xsl:for-each> <!-- Now handle the case where there are NO <d> elements --> <xsl:if test="not(d)"> <xsl:value-of select="@name"/> <xsl:text>,</xsl:text> <xsl:value-of select="c/@value"/> <xsl:text>,</xsl:text> <xsl:value-of select="c"/> <xsl:text>,</xsl:text> <xsl:text>,</xsl:text> <!-- Empty <d> value --> <xsl:value-of select="e"/> <xsl:text> </xsl:text> </xsl:if> </xsl:template> </xsl:stylesheet>
Key Improvements Explained
- Dual handling: We first loop through all
<d>elements (just like your original code) to generate rows for each existing<d>. Then we add an<xsl:if test="not(d)">block that triggers only when a<b>has no<d>children, outputting a row with an empty value in the<d>column. - Accessing sibling/parent data: Your original approach to use
../to access the parent<b>element, then reference its other children (c,e) is perfectly correct in XSLT. This is how you "reach back" to get sibling node data while processing a<d>element.
Output Result
When run against your sample XML, this stylesheet will produce exactly your desired CSV:
first,C Value,C Text,D1 Text,E Text first,C Value,C Text,D2 Text,E Text second,C Value,C Text,D1 Text,E Text second,C Value,C Text,D2 Text,E Text second,C Value,C Text,D3 Text,E Text third,C Value,C Text,,E Text
Why XSLT is a Good Fit Here
XSLT is designed specifically for XML transformations like this. Unlike your Python/lxml approach (which is totally valid!), XSLT lets you express the transformation logic in a declarative way that's tightly aligned with XML structure. It handles variable element counts (like the varying number of <d>s) naturally with loops and conditional checks, and it's widely supported across tools and platforms.
内容的提问来源于stack exchange,提问作者Rusty Lemur

