如何使用XSLT 1.0将HTML UL树形结构转换为一维XML?
嵌套聊天HTML转扁平化XML解决方案
需求说明
需要将嵌套的聊天树形HTML结构转换为带父ID(pid)的扁平化XML列表,实现消息层级的扁平化映射。
输入HTML结构
<!DOCTYPE html> <html> <head> <title>Chat</title> </head> <body> <ul> <li> <b>Name 1</b> say: Hi!<ul> <li> <b>Name 2</b> say: Hello<ul> <li> <b>Name 3</b> say: Ho ho </li> <li> <b>Name 4</b> say: How do you do?<ul> <li> <b>Name 5</b> say: I'm fine<ul> <li> <b>Name 4</b> say: Ok </li> </ul> </li> </ul> </li> </ul> </li> <li> <b>Name 3</b> say: Hi. How do you do?<ul> <li> <b>Name 1</b> say: Fine </li> </ul> </li> </ul> </li> </ul> </body> </html>
期望输出XML结构
<?xml version="1.0" encoding="UTF-8"?> <items> <item id="1" pid="0" name="Name 1">Hi!</item> <item id="2" pid="1" name="Name 2">Hello</item> <item id="3" pid="2" name="Name 3">Ho ho</item> <item id="4" pid="2" name="Name 4">How do you do?</item> <item id="5" pid="4" name="Name 5">I'm fine</item> <item id="6" pid="5" name="Name 4">Ok</item> <item id="7" pid="1" name="Name 3">Hi. How do you do?</item> <item id="8" pid="7" name="Name 1">Fine</item> </items>
原尝试的XSLT问题
原代码仅匹配<b>标签,未处理嵌套结构的递归遍历,也未生成id和pid属性,无法完成完整转换:
<xsl:stylesheet version="1.0" encoding="utf-8" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="b"> <item> <xsl:attribute name="name"> <xsl:value-of select="@*|node()"/> </xsl:attribute> </item> </xsl:template> </xsl:stylesheet>
正确的XSLT解决方案
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 根模板:初始化XML结构,开始处理最外层聊天消息 --> <xsl:template match="/html"> <items> <xsl:apply-templates select="body/ul/li"> <xsl:with-param name="parent-id" select="0"/> </xsl:apply-templates> </items> </xsl:template> <!-- 核心模板:处理每个聊天消息项(li元素) --> <xsl:template match="li"> <xsl:param name="parent-id"/> <!-- 生成当前消息的唯一ID,按所有li的出现顺序计数 --> <xsl:variable name="current-id"> <xsl:number level="any" count="li"/> </xsl:variable> <!-- 提取发送者名称 --> <xsl:variable name="sender-name" select="b/text()"/> <!-- 提取消息内容:移除"say: "前缀并清理空格 --> <xsl:variable name="message-content" select="substring-after(normalize-space(text()), 'say: ')"/> <!-- 生成扁平化的item节点 --> <item id="{$current-id}" pid="{$parent-id}" name="{$sender-name}"> <xsl:value-of select="$message-content"/> </item> <!-- 递归处理当前消息的子回复 --> <xsl:apply-templates select="ul/li"> <xsl:with-param name="parent-id" select="$current-id"/> </xsl:apply-templates> </xsl:template> <!-- 忽略无关节点,避免输出冗余内容 --> <xsl:template match="head | title | text()[not(parent::li)]"/> </xsl:stylesheet>
代码说明
- 根模板:匹配整个HTML文档,创建XML根节点
<items>,并启动最外层<li>的处理,设置根消息的父ID为0。 - 核心li处理模板:
- 用
<xsl:number>为每个<li>生成连续唯一的ID,保证顺序与原聊天层级一致。 - 提取
<b>标签内的文本作为发送者名称,通过substring-after截取"say: "后的内容作为消息正文。 - 递归处理当前
<li>内部的子回复,将当前ID传递给子消息作为父ID(pid)。
- 用
- 无关节点过滤:忽略
<head>、<title>等不需要的节点,避免输出无效内容。
使用该XSLT处理输入HTML,即可得到符合要求的扁平化XML结构。
内容的提问来源于stack exchange,提问作者Andre
相关产品推荐
相关产品推荐

