使用Saxon与XSLT 3.0实现指定XML节点按序插入合并的方案与样式表示例
Saxon与XSLT 3.0实现指定XML节点按序插入合并的方案与样式表示例
嘿,我帮你梳理了这个XML节点合并的需求,用XSLT 3.0配合Saxon就能完美解决,下面是完整的方案、代码和细节说明:
先明确咱们的核心需求
- 把
merge.xml里的每个<mergeinto>/<mergecontent>节点,按顺序对应插入到merge-test.xml的每个<entry>节点中 - 插入位置固定在
<title>和<content type='html'>之间 - 两个文件的节点数量完全匹配,直接按位置对应即可,不需要排序
- 最终输出结构保持
merge-test.xml的Atom feed格式,保存为output.xml
补全完整的merge.xml示例(你之前的输入不完整,我帮你补全了)
<?xml version='1.0' encoding='utf-8'?> <feed xmlns='http://www.w3.org/2005/Atom' xmlns:blogger='http://schemas.google.com/blogger/2018'> <mergeinto> <mergecontent>Lorem Ipsum Content 1</mergecontent> </mergeinto> <mergeinto> <mergecontent>Lorem Ipsum Content 2</mergecontent> </mergeinto> <mergeinto> <mergecontent>Lorem Ipsum Content 3</mergecontent> </mergeinto> <mergeinto> <mergecontent>Lorem Ipsum Content 4</mergecontent> </mergeinto> </feed>
完整的XSLT 3.0样式表merge-stylesheet.xsl
我写了两种插入逻辑的实现,你可以根据需求选一种:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.w3.org/2005/Atom" xmlns:blogger="http://schemas.google.com/blogger/2018" exclude-result-prefixes="#all"> <!-- 提前加载外部merge.xml的所有合并内容 --> <xsl:variable name="merge-content-list" select="document('merge.xml')/*/mergeinto/mergecontent" /> <!-- 身份模板:默认复制所有节点和属性,保持原文档结构 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 方案一:按节点整体顺序插入(适合entry内节点顺序固定的场景) --> <xsl:template match="entry"> <xsl:copy> <!-- 先复制entry里除content外的所有节点 --> <xsl:apply-templates select="@*|node()[not(self::content)]"/> <!-- 插入当前entry对应的mergecontent --> <mergecontent> <xsl:value-of select="$merge-content-list[position() = current()/position()]"/> </mergecontent> <!-- 最后复制原有的content节点 --> <xsl:apply-templates select="content"/> </xsl:copy> </xsl:template> <!-- 方案二:精准在title之后插入(推荐!即使entry内其他节点顺序变化也不影响位置) --> <!-- 如果你需要更精准的插入位置,注释掉上面的entry模板,打开下面这两个模板 --> <!-- <xsl:template match="entry"> <xsl:copy> <xsl:apply-templates select="@*|node()"> <!-- 用隧道参数传递当前entry对应的merge内容 --> <xsl:with-param name="target-merge-content" select="$merge-content-list[position() = current()/position()]" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="title"> <xsl:param name="target-merge-content" tunnel="yes"/> <!-- 先复制原有的title节点 --> <xsl:copy-of select="."/> <!-- 立刻插入mergecontent --> <mergecontent> <xsl:value-of select="$target-merge-content"/> </mergecontent> </xsl:template> --> </xsl:stylesheet>
关键细节拆解(帮你避坑)
- 命名空间处理:用
xpath-default-namespace指定默认的Atom命名空间,避免每次写元素都要加前缀,解决XML命名空间匹配的常见坑 - 位置对应逻辑:
$merge-content-list[position() = current()/position()]让每个entry严格对应同位置的mergecontent节点,完美匹配你的顺序需求 - 两种方案的区别:
- 方案一写法简单,适合entry内节点顺序固定的场景
- 方案二用隧道参数精准控制插入位置,即使entry里的其他节点顺序变动,mergecontent也会牢牢插在title后面
用Saxon运行的命令行
确保你已经下载了Saxon的JAR包(比如saxon-he-12.4.jar),打开命令行执行:
java -jar saxon-he-12.4.jar -s:merge-test.xml -xsl:merge-stylesheet.xsl -o:output.xml
最终输出的output.xml片段示例
每个entry里都会在title和content之间出现对应的mergecontent:
<entry> <id>tag:blogger.com,1999:blog-17477.post-670855911</id> <blogger:type>POST</blogger:type> <blogger:status>LIVE</blogger:status> <author> <name>Author</name> <uri>Author Link</uri> <blogger:type>BLOGGER</blogger:type> </author> <title>Title Title</title> <mergecontent>Lorem Ipsum Content 1</mergecontent> <content type='html'>Content Content Content Content Content</content> <!-- 其他原有节点保持不变 --> </entry>
如果需要调整mergecontent的命名空间、标签名,或者修改插入位置,直接改样式表的对应部分就行,非常灵活~




