使用XSLT2.0基于两个XML生成目标XML的技术问题
修正XSLT2.0逻辑以实现正确的XML匹配输出
我看了你的问题,核心问题在于你当前的XSLT把所有匹配的city节点都塞进了同一个detail标签里,而不是对应第二个XML里的每个parent节点生成独立的detail标签。下面是整理后的问题细节和修正后的解决方案:
输入数据源
数据源1:数据库人员详情
<parent> <child> <name>John</name> <city>Boston</city> </child> <child> <name>John</name> <city>Seattle</city> </child> <child> <name>Allison</name> <city>Houston</city> </child> </parent>
数据源2:关联城市代码
<details> <parent> <detail> <city>Boston</city> <code>abc</code> </detail> <detail> <city>Houston</city> <code>xyz</code> </detail> </parent> <parent> <detail> <city>Boston</city> <code>abc</code> </detail> <detail> <city>Seattle</city> <code>mno</code> </detail> </parent> <parent> <detail> <city>Houston</city> <code>xyz</code> </detail> <detail> <city>Seattle</city> <code>mno</code> </detail> </parent> </details>
目标输出要求
- 按数据库XML中唯一name生成对应数量的
<Data>标签(已实现) - 每个人员匹配其所有唯一城市,在第二个XML的所有
<parent>标签中匹配这些城市:- 每个匹配的
<parent>标签生成一个独立的<detail>子标签 - 若某个
<parent>中没有匹配当前人员的城市,则跳过该<parent> - 无任何匹配的
<parent>时,不生成<details>标签
- 每个匹配的
当前错误输出
<FinalData> <Data> <name>John</name> <details> <detail> <city value="Boston">abc</city> <city value="Boston">abc</city> <city value="Seattle">mno</city> <city value="Seattle">mno</city> </detail> </details> </Data> <Data> <name>Allison</name> <details> <detail> <city value="Houston">xyz</city> <city value="Houston">xyz</city> </detail> </details> </Data> </FinalData>
期望输出
<FinalData> <Data> <name>John</name> <details> <detail> <city value="Boston">abc</city> </detail> <detail> <city value="Boston">abc</city> <city value="Seattle">mno</city> </detail> <detail> <city value="Seattle">mno</city> </detail> </details> </Data> <Data> <name>Allison</name> <details> <detail> <city value="Houston">xyz</city> </detail> <detail> <city value="Houston">xyz</city> </detail> </details> </Data> </FinalData>
修正后的XSLT2.0代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 引入第二个数据源,替换为你的实际文件路径 --> <xsl:variable name="detailSource" select="document('path/to/your/second.xml')"/> <!-- 主模板生成根节点 --> <xsl:template match="/"> <FinalData> <!-- 按name分组处理人员数据 --> <xsl:for-each-group select="parent/child" group-by="name"> <Data> <name><xsl:value-of select="current-grouping-key()"/></name> <!-- 获取当前人员的所有唯一城市 --> <xsl:variable name="personCities" select="distinct-values(current-group()/city)"/> <!-- 筛选第二个数据源中包含当前人员城市的parent节点 --> <xsl:variable name="matchingParents" select="$detailSource/details/parent[detail/city = $personCities]"/> <!-- 仅在有匹配的parent时生成details标签 --> <xsl:if test="$matchingParents"> <details> <!-- 遍历每个匹配的parent,生成独立的detail标签 --> <xsl:for-each select="$matchingParents"> <detail> <!-- 填充当前parent中属于当前人员城市的detail内容 --> <xsl:for-each select="detail[city = $personCities]"> <city value="{city}"><xsl:value-of select="code"/></city> </xsl:for-each> </detail> </xsl:for-each> </details> </xsl:if> </Data> </xsl:for-each-group> </FinalData> </xsl:template> </xsl:stylesheet>
关键逻辑说明
- 人员分组:用
<xsl:for-each-group>按name分组,确保每个唯一姓名对应一个<Data>标签。 - 唯一城市提取:通过
distinct-values()获取当前人员的不重复城市,避免无效的重复匹配。 - 精准匹配parent:筛选第二个数据源中至少包含一个当前人员城市的
<parent>节点,只处理相关的分组。 - 独立detail生成:遍历每个匹配的
<parent>,为其生成独立的<detail>标签,再填充该<parent>内所有匹配的城市数据。 - 空值处理:只有存在匹配的
<parent>时才生成<details>标签,符合需求中的“无匹配则不生成details标签”规则。
这样就能完美生成你期望的XML结构了,每个<parent>对应一个<detail>,不会出现所有城市数据堆叠的问题。
内容的提问来源于stack exchange,提问作者karD
相关产品推荐
相关产品推荐

