如何用XSLT提取XML中Subreport嵌套元素的名称与属性
问题描述
处理CrystalReport格式的XML文件,需要通过XSLT输出元素名称及其属性。当前转换已实现部分功能,但无法识别Subreport节点内的嵌套元素,需修改XSLT以包含这部分内容。
原始XML文件
<?xml version="1.0" encoding="UTF-8" ?> <CrystalReport> <Details Level="2"> <Section SectionNumber="0"> <Field Name="IDNUMBER1"> <Value>xxx</Value> </Field> <Field Name="TITLE1"> <Value>aaa</Value> </Field> <Subreport Name="Subreport1"> <ReportHeader> </ReportHeader> <Details Level="1"> <Section SectionNumber="0"> <Field Name="LASTSUFFNAME1"> <Value>bbb</Value> </Field> <Field Name="FIRSTMIDNAME1"> <Value>ccc</Value> </Field> </Section> </Details> <Details Level="1"> <Section SectionNumber="0"> <Field Name="LASTSUFFNAME1"> <Value>ddd</Value> </Field> <Field Name="FIRSTMIDNAME1"> <Value>eee</Value> </Field> </Section> </Details> </Subreport> <Field Name="MKEY1"> <FormattedValue>111</FormattedValue> <Value>111</Value> </Field> <Field Name="DATE21"> <Value>2023-07-13</Value> </Field> </Section> </Details> </CrystalReport>
当前使用的XSLT代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="CrystalReport"> <set> <xsl:apply-templates/> </set> </xsl:template> <xsl:template match="Details|Details/Section/Subreport/node()"> <xsl:apply-templates select="Section/*"/> </xsl:template> <xsl:template match="Section/*"> <xsl:for-each select="."> <attributeName> <xsl:value-of select="local-name()"/> <xsl:text> </xsl:text> <xsl:value-of select="@*"/> </attributeName> </xsl:for-each> </xsl:template> </xsl:stylesheet>
当前输出结果
<set> <attributeName>Field IDNUMBER1</attributeName> <attributeName>Field TITLE1</attributeName> <attributeName>Subreport </attributeName> <attributeName>Field MKEY1</attributeName> <attributeName>Field DATE21</attributeName> </set>
期望输出结果
<set> <attributeName>Field IDNUMBER1</attributeName> <attributeName>Field TITLE1</attributeName> <attributeName>Field LASTSUFFNAME1</attributeName> <attributeName>Field FIRSTMIDNAME1</attributeName> <attributeName>Field LASTSUFFNAME1</attributeName> <attributeName>Field FIRSTMIDNAME1</attributeName> <attributeName>Field MKEY1</attributeName> <attributeName>Field DATE21</attributeName> </set>
解决方案
问题出在原XSLT的模板匹配逻辑:未递归处理Subreport内部的Details节点,且错误输出了Subreport本身的信息。修改后的XSLT代码如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="CrystalReport"> <set> <xsl:apply-templates/> </set> </xsl:template> <!-- 匹配所有层级的Details节点,处理其下的Section --> <xsl:template match="Details"> <xsl:apply-templates select="Section"/> </xsl:template> <!-- 匹配Subreport节点,直接处理内部内容,跳过自身输出 --> <xsl:template match="Subreport"> <xsl:apply-templates/> </xsl:template> <!-- 匹配Section节点,处理其下所有子元素 --> <xsl:template match="Section"> <xsl:apply-templates select="*"/> </xsl:template> <!-- 专门匹配Field节点,生成目标输出 --> <xsl:template match="Field"> <attributeName> <xsl:value-of select="local-name()"/> <xsl:text> </xsl:text> <xsl:value-of select="@Name"/> </attributeName> </xsl:template> <!-- 忽略不需要处理的节点(如ReportHeader、Value等) --> <xsl:template match="*[not(self::Field or self::Details or self::Section or self::Subreport)]"/> </xsl:stylesheet>
代码说明
- 单独匹配
Subreport节点,直接递归处理内部所有内容,不输出Subreport自身信息 - 匹配所有层级的
Details节点,确保Subreport内的Details也能被处理 - 针对
Field节点单独生成输出,明确取@Name属性,避免意外匹配其他属性 - 新增模板忽略无关节点,避免干扰最终输出
修改后即可正确捕获Subreport内部的所有Field元素,生成符合预期的输出结果。
内容的提问来源于stack exchange,提问作者IIAND
相关产品推荐
相关产品推荐

