如何编写适配两种XML结构的XSLT 2.0转换脚本?
需求说明
需要用XSLT 2.0转换两种结构不同的XML输入,得到统一的指定输出。现有基于for-each-group的脚本能正常处理第一种XML,但处理第二种时会出现MATNR重复的错误。
第一种XML结构
<DELIVERY> <E1EDP07> <E1EDP08> <EXIDV>SSCC_CODE_C1</EXIDV> <PCKAR>CARTON</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_P1</EXIDV> <PCKAR>WP</PCKAR> </E1EDP08> <E1EDP09> <MATNR>PRODUCT_A</MATNR> </E1EDP09> </E1EDP07> <E1EDP07> <E1EDP08> <EXIDV>SSCC_CODE_C1</EXIDV> <PCKAR>CARTON</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_P1</EXIDV> <PCKAR>WP</PCKAR> </E1EDP08> <E1EDP09> <MATNR>PRODUCT_B</MATNR> </E1EDP09> </E1EDP07> <E1EDP07> <E1EDP08> <EXIDV>SSCC_CODE_C2</EXIDV> <PCKAR>CARTON</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_P2</EXIDV> <PCKAR>WP</PCKAR> </E1EDP08> <E1EDP09> <MATNR>PRODUCT_C</MATNR> </E1EDP09> </E1EDP07> </DELIVERY>
第二种XML结构
<DELIVERY> <E1EDP07> <E1EDP08> <EXIDV>SSCC_CODE_C1</EXIDV> <PCKAR>CARTON</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_P1</EXIDV> <PCKAR>WP</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_C1</EXIDV> <PCKAR>CARTON</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_P1</EXIDV> <PCKAR>WP</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_C2</EXIDV> <PCKAR>CARTON</PCKAR> </E1EDP08> <E1EDP08> <EXIDV>SSCC_CODE_P2</EXIDV> <PCKAR>WP</PCKAR> </E1EDP08> <E1EDP09> <MATNR>PRODUCT_A</MATNR> </E1EDP09> <E1EDP09> <MATNR>PRODUCT_B</MATNR> </E1EDP09> <E1EDP09> <MATNR>PRODUCT_C</MATNR> </E1EDP09> </E1EDP07> </DELIVERY>
期望输出
<EXIDV>SSCC_CODE_C1</EXIDV> <MATNR>PRODUCT_A</MATNR> <MATNR>PRODUCT_B</MATNR> <EXIDV>SSCC_CODE_C2</EXIDV> <MATNR>PRODUCT_C</MATNR>
当前使用的XSLT 2.0脚本
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <DELIVERY> <xsl:for-each-group select="DELIVERY/E1EDP07/E1EDP08[PCKAR='CARTON']" group-by="EXIDV" > <EXIDV> <xsl:value-of select="EXIDV"/> </EXIDV> <xsl:for-each select="current-group()"> <MATNR> <xsl:value-of select="../E1EDP09/MATNR"/> </MATNR> </xsl:for-each> </xsl:for-each-group> </DELIVERY> </xsl:template> </xsl:stylesheet>
问题分析
原脚本的问题在于:
- 第一种XML中,每个
<E1EDP07>下只有一个<E1EDP09>,../E1EDP09/MATNR只会取到一个值; - 第二种XML中,单个
<E1EDP07>下有多个<E1EDP09>,且对应多个<E1EDP08>(CARTON类型),此时../E1EDP09/MATNR会返回所有MATNR值,加上遍历current-group()时重复处理同组条目,导致MATNR重复输出。
修改后的XSLT脚本
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <DELIVERY> <!-- 按EXIDV分组所有CARTON类型的E1EDP08 --> <xsl:for-each-group select="DELIVERY/E1EDP07/E1EDP08[PCKAR='CARTON']" group-by="EXIDV"> <EXIDV> <xsl:value-of select="EXIDV"/> </EXIDV> <!-- 获取当前组所有条目对应的MATNR并去重 --> <xsl:for-each select="distinct-values(current-group()/../E1EDP09/MATNR)"> <MATNR> <xsl:value-of select="."/> </MATNR> </xsl:for-each> </xsl:for-each-group> </DELIVERY> </xsl:template> </xsl:stylesheet>
脚本说明
- 保留原有的按
EXIDV分组逻辑,确保同一SSCC_CODE的CARTON被归为一组; - 用
distinct-values(current-group()/../E1EDP09/MATNR)替换原遍历逻辑:current-group()/../E1EDP09/MATNR获取当前组内所有CARTON条目所属<E1EDP07>下的所有MATNR值;distinct-values()函数自动去重,避免重复输出相同的MATNR;
- 该逻辑适配两种XML结构,无论每个
<E1EDP07>下对应单个还是多个MATNR,都能输出唯一的MATNR列表。
内容的提问来源于stack exchange,提问作者RH007
相关产品推荐
相关产品推荐

