如何用XSLT 1.0实现XML子标签转属性并保留Sx层级嵌套
XML报文转换需求
需要将输入XML转换为带多属性的嵌套XML结构,转换规则如下:
- 所有S1、S2、S3直至S9层级的标签原样保留为元素
- 其余子标签需转换为对应父级S标签的属性
- 嵌套深度最高可达9层(S1到S9)
输入XML示例
<S1> <P>23432</P> <Su>BG</Su> <S2> <P>cc</P> <Su>B</Su> <S3> <P>427013947</P> <Su>RM</Su> <F>MDF</F> <I>427113948</I> <C>true</C> <T></T> <A>xxx</A> </S3> <F></F> <I>427013947</I> <C>true</C> <T></T> <A>xxxsx</A> </S2> <S2> <P>cc</P> <Su>FL</Su> <F>1</F> <I>427814536</I> <C>true</C> <T></T> <A>xxxsx</A> </S2> <S2> <P>cc</P> <Su>G</Su> <F></F> <I>444</I> <C>true</C> <T></T> <A>xxxsx</A> </S2> <F>1</F> <I>cc</I> <C>true</C> <T></T> <A>xxxs</A> <B></B> </S1>
期望输出XML示例
<S1 P="23432" Su="BG" F="1" I="cc" C="true" T="" A="xxxs" B=""> <S2 P="cc" Su="B" F="" I="427013947" C="true" T="" A="xxxsx"> <S3 P="427013947" Su="RM" F="MDF" I="427113948" C="true" T="" A="xxx"></S3> </S2> <S2 P="cc" Su="FL" F="1" I="427814536" C="true" T="" A="xxxsx"></S2> <S2 P="cc" Su="G" F="" I="444" C="true" T="" A="xxxsx"></S2> </S1>
现有未完成的XSLT代码
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes" encoding="UTF-8" /> <xsl:template match="//S1"> <xsl:element name="S1"> <xsl:for-each select="*"> <xsl:if test="local-name()!='S1'"> <xsl:attribute name="{name()}" > <xsl:value-of select="text()" /> </xsl:attribute> </xsl:if> </xsl:for-each> <xsl:for-each select="S2"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:for-each> </xsl:element> </xsl:template></xsl:stylesheet>
XSLT 1.0 转换脚本实现
以下是符合需求的完整转换代码:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="xml" indent="yes" encoding="UTF-8" /> <!-- 匹配S1~S9所有层级节点 --> <xsl:template match="*[starts-with(name(), 'S') and string-length(name())=2 and substring(name(),2,1) >= '1' and substring(name(),2,1) <= '9']"> <xsl:element name="{name()}"> <!-- 转换非S层级子节点为属性 --> <xsl:for-each select="*[not(starts-with(name(), 'S') and string-length(name())=2 and substring(name(),2,1) >= '1' and substring(name(),2,1) <= '9')]"> <xsl:attribute name="{name()}"> <xsl:value-of select="text()" /> </xsl:attribute> </xsl:for-each> <!-- 递归处理子级S节点 --> <xsl:apply-templates select="*[starts-with(name(), 'S') and string-length(name())=2 and substring(name(),2,1) >= '1' and substring(name(),2,1) <= '9']" /> </xsl:element> </xsl:template> <!-- 过滤原XML中的空白文本节点 --> <xsl:template match="text()" /> </xsl:stylesheet>
实现逻辑说明
- 节点匹配规则通过「名称以S开头、长度为2、第二个字符为1-9的数字」三个条件精准命中S1到S9所有层级的节点,无需为每个S层级单独写模板
- 对每个命中的S层级节点,先遍历所有非S层级的子节点,将标签名作为属性名、文本内容作为属性值挂载到当前S节点上
- 递归应用模板处理当前节点下的子S层级节点,自动支持最高9层的嵌套转换
- 空白文本节点匹配规则用来过滤原XML中的缩进、换行等无效内容,保证输出格式整洁
内容的提问来源于stack exchange,提问作者Dil
相关产品推荐
相关产品推荐

