XSLT 将 XML 两部分之间的数据有条件地合并到第二部分
XSLT merging data between two parts of XML conditionally into the second part
对于我在不眠之夜中苦苦挣扎的以下问题,我将不胜感激(XSLT 代码)。
消息的两部分(Message1 和 Message2)都包含各种 ID 的人工数据。 Message1 可能包含相同 id 的相同 human id 的数据和附加段 <action>
(值 'New' 或 'Old')。对于 Message2 中的所有人类 ID,如果 'same' 块与 action='New'
一起存在,则需要检查 Message1。如果是,则需要将相关节点的 <action>New</action>
复制到 Message2 相关节点中(或者 Message1 的整个相关节点需要在结构的正确位置替换 Message2 中的 'same' 节点) .期望的结果是基于这些条件合并到 Message2 中,如下所示。仅用于动作 New 且仅需要合并相同的块。
源测试用例是这样的:
<Messages>
<Message1>
<Response>
<CE>
<id>1</id>
<human>
<name>Frank</name>
<human_information>
<action>New</action>
<title>Doctor</title>
</human_information>
<phone>
<action>Old</action>
<phone_number>1234567</phone_number>
</phone>
</human>
</CE>
</Response>
<Response>
<CE>
<id>2</id>
<human>
<name>Bob</name>
<human_information>
<action>New</action>
<title>Artist</title>
</human_information>
<phone>
<action>Old</action>
<phone_number>13579</phone_number>
</phone>
</human>
</CE>
</Response>
<Response>
<CE>
<id>3</id>
<human>
<name>Alice</name>
<human_information>
<action>Old</action>
<title>Designer</title>
</human_information>
<phone>
<action>New</action>
<phone_number>9876543</phone_number>
</phone>
</human>
</CE>
</Response>
</Message1>
<Message2>
<Response>
<CE>
<id>2</id>
<human>
<name>Bob</name>
<human_information>
<title>Artist</title>
</human_information>
<phone>
<phone_number>13579</phone_number>
</phone>
<phone>
<phone_number>24680</phone_number>
</phone>
</human>
</CE>
</Response>
<Response>
<CE>
<id>3</id>
<human>
<name>Alice</name>
<human_information>
<title>Designer</title>
</human_information>
<phone>
<phone_number>9876543</phone_number>
</phone>
<phone>
<phone_number>0909090</phone_number>
</phone>
</human>
</CE>
</Response>
</Message2>
</Messages>
想要的测试结果应该是这样的:
<Response>
<CE>
<id>2</id>
<human>
<name>Bob</name>
<human_information>
<action>New</action>
<title>Artist</title>
</human_information>
<phone>
<phone_number>13579</phone_number>
</phone>
<phone>
<phone_number>24680</phone_number>
</phone>
</human>
</CE>
<CE>
<id>3</id>
<human>
<name>Alice</name>
<human_information>
<action>New</action>
<title>Designer</title>
</human_information>
<phone>
<action>New</action>
<phone_number>9876543</phone_number>
</phone>
<phone>
<phone_number>0909090</phone_number>
</phone>
</human>
</CE>
</Response>
这是使用 Altova 2018 的 XSLT 3 尝试:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:key name="map" match="Message1/Response/CE//*[action = 'New']" composite="yes">
<xsl:sequence select="ancestor::CE/id"/>
<xsl:sequence select="node-name()"/>
<xsl:variable name="pos" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:sequence select="$pos"/>
</xsl:key>
<xsl:template match="Messages">
<xsl:apply-templates select="Message2"/>
</xsl:template>
<xsl:template match="Message2">
<Response>
<xsl:apply-templates select="Response/*"/>
</Response>
</xsl:template>
<xsl:template match="Message2/Response/CE//*[key('map', (ancestor::CE/id, node-name(), count((., preceding-sibling::*[node-name() = node-name(current())]))))]">
<xsl:copy-of select="key('map', (ancestor::CE/id, node-name(), count((., preceding-sibling::*[node-name() = node-name(current())]))))"/>
</xsl:template>
</xsl:stylesheet>
不幸的是,Saxon 9.8.0.14 给了我关于密钥 "A sequence of more than one item is not allowed as the @use attribute of xsl:key" 的警告,然后也反对将密钥与 "Key definition is circular" 一起使用,这是我在 Saxon 支持论坛上提出的问题分别导致错误报告 https://saxonica.plan.io/issues/3861。
由于 Saxon 的问题似乎是由在 xsl:key
声明中使用序列构造函数引起的,我重写了使用 use
表达式的方法,排除了 xsl:number
转换为函数:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs map mf"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:function name="mf:number" as="xs:integer">
<xsl:param name="element" as="element()"/>
<xsl:for-each select="$element">
<xsl:number/>
</xsl:for-each>
</xsl:function>
<xsl:key name="map" match="Message1/Response/CE//*[action = 'New']" composite="yes" use="ancestor::CE/id, node-name(), mf:number(.)"/>
<xsl:template match="Messages">
<xsl:apply-templates select="Message2"/>
</xsl:template>
<xsl:template match="Message2">
<Response>
<xsl:apply-templates select="Response/*"/>
</Response>
</xsl:template>
<xsl:template match="Message2/Response/CE//*[key('map', (ancestor::CE/id, node-name(), mf:number(.)))]">
<xsl:copy-of select="key('map', (ancestor::CE/id, node-name(), mf:number(.)))"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzQx/5
尝试使用 XSLT 2 解决该问题会导致长键表达式
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="map"
match="Message1/Response/CE//*[action = 'New']"
use="concat(ancestor::CE/id, '|', node-name(.), '|', count((., preceding-sibling::*[node-name(.) = node-name(current())])))"/>
<xsl:template match="Messages">
<xsl:apply-templates select="Message2"/>
</xsl:template>
<xsl:template match="Message2">
<Response>
<xsl:apply-templates select="Response/*"/>
</Response>
</xsl:template>
<xsl:template match="Message2/Response/CE//*[key('map', concat(ancestor::CE/id, '|', node-name(.), '|', count((., preceding-sibling::*[node-name(.) = node-name(current())]))))]">
<xsl:copy-of select="key('map', concat(ancestor::CE/id, '|', node-name(.), '|', count((., preceding-sibling::*[node-name(.) = node-name(current())]))))"/>
</xsl:template>
</xsl:transform>
但希望能解决 http://xsltransform.hikmatu.com/pPgCcoA 中的问题,即使是旧版本的 Saxon。
对于我在不眠之夜中苦苦挣扎的以下问题,我将不胜感激(XSLT 代码)。
消息的两部分(Message1 和 Message2)都包含各种 ID 的人工数据。 Message1 可能包含相同 id 的相同 human id 的数据和附加段 <action>
(值 'New' 或 'Old')。对于 Message2 中的所有人类 ID,如果 'same' 块与 action='New'
一起存在,则需要检查 Message1。如果是,则需要将相关节点的 <action>New</action>
复制到 Message2 相关节点中(或者 Message1 的整个相关节点需要在结构的正确位置替换 Message2 中的 'same' 节点) .期望的结果是基于这些条件合并到 Message2 中,如下所示。仅用于动作 New 且仅需要合并相同的块。
源测试用例是这样的:
<Messages>
<Message1>
<Response>
<CE>
<id>1</id>
<human>
<name>Frank</name>
<human_information>
<action>New</action>
<title>Doctor</title>
</human_information>
<phone>
<action>Old</action>
<phone_number>1234567</phone_number>
</phone>
</human>
</CE>
</Response>
<Response>
<CE>
<id>2</id>
<human>
<name>Bob</name>
<human_information>
<action>New</action>
<title>Artist</title>
</human_information>
<phone>
<action>Old</action>
<phone_number>13579</phone_number>
</phone>
</human>
</CE>
</Response>
<Response>
<CE>
<id>3</id>
<human>
<name>Alice</name>
<human_information>
<action>Old</action>
<title>Designer</title>
</human_information>
<phone>
<action>New</action>
<phone_number>9876543</phone_number>
</phone>
</human>
</CE>
</Response>
</Message1>
<Message2>
<Response>
<CE>
<id>2</id>
<human>
<name>Bob</name>
<human_information>
<title>Artist</title>
</human_information>
<phone>
<phone_number>13579</phone_number>
</phone>
<phone>
<phone_number>24680</phone_number>
</phone>
</human>
</CE>
</Response>
<Response>
<CE>
<id>3</id>
<human>
<name>Alice</name>
<human_information>
<title>Designer</title>
</human_information>
<phone>
<phone_number>9876543</phone_number>
</phone>
<phone>
<phone_number>0909090</phone_number>
</phone>
</human>
</CE>
</Response>
</Message2>
</Messages>
想要的测试结果应该是这样的:
<Response>
<CE>
<id>2</id>
<human>
<name>Bob</name>
<human_information>
<action>New</action>
<title>Artist</title>
</human_information>
<phone>
<phone_number>13579</phone_number>
</phone>
<phone>
<phone_number>24680</phone_number>
</phone>
</human>
</CE>
<CE>
<id>3</id>
<human>
<name>Alice</name>
<human_information>
<action>New</action>
<title>Designer</title>
</human_information>
<phone>
<action>New</action>
<phone_number>9876543</phone_number>
</phone>
<phone>
<phone_number>0909090</phone_number>
</phone>
</human>
</CE>
</Response>
这是使用 Altova 2018 的 XSLT 3 尝试:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:key name="map" match="Message1/Response/CE//*[action = 'New']" composite="yes">
<xsl:sequence select="ancestor::CE/id"/>
<xsl:sequence select="node-name()"/>
<xsl:variable name="pos" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:sequence select="$pos"/>
</xsl:key>
<xsl:template match="Messages">
<xsl:apply-templates select="Message2"/>
</xsl:template>
<xsl:template match="Message2">
<Response>
<xsl:apply-templates select="Response/*"/>
</Response>
</xsl:template>
<xsl:template match="Message2/Response/CE//*[key('map', (ancestor::CE/id, node-name(), count((., preceding-sibling::*[node-name() = node-name(current())]))))]">
<xsl:copy-of select="key('map', (ancestor::CE/id, node-name(), count((., preceding-sibling::*[node-name() = node-name(current())]))))"/>
</xsl:template>
</xsl:stylesheet>
不幸的是,Saxon 9.8.0.14 给了我关于密钥 "A sequence of more than one item is not allowed as the @use attribute of xsl:key" 的警告,然后也反对将密钥与 "Key definition is circular" 一起使用,这是我在 Saxon 支持论坛上提出的问题分别导致错误报告 https://saxonica.plan.io/issues/3861。
由于 Saxon 的问题似乎是由在 xsl:key
声明中使用序列构造函数引起的,我重写了使用 use
表达式的方法,排除了 xsl:number
转换为函数:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs map mf"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:function name="mf:number" as="xs:integer">
<xsl:param name="element" as="element()"/>
<xsl:for-each select="$element">
<xsl:number/>
</xsl:for-each>
</xsl:function>
<xsl:key name="map" match="Message1/Response/CE//*[action = 'New']" composite="yes" use="ancestor::CE/id, node-name(), mf:number(.)"/>
<xsl:template match="Messages">
<xsl:apply-templates select="Message2"/>
</xsl:template>
<xsl:template match="Message2">
<Response>
<xsl:apply-templates select="Response/*"/>
</Response>
</xsl:template>
<xsl:template match="Message2/Response/CE//*[key('map', (ancestor::CE/id, node-name(), mf:number(.)))]">
<xsl:copy-of select="key('map', (ancestor::CE/id, node-name(), mf:number(.)))"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/nc4NzQx/5
尝试使用 XSLT 2 解决该问题会导致长键表达式
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="map"
match="Message1/Response/CE//*[action = 'New']"
use="concat(ancestor::CE/id, '|', node-name(.), '|', count((., preceding-sibling::*[node-name(.) = node-name(current())])))"/>
<xsl:template match="Messages">
<xsl:apply-templates select="Message2"/>
</xsl:template>
<xsl:template match="Message2">
<Response>
<xsl:apply-templates select="Response/*"/>
</Response>
</xsl:template>
<xsl:template match="Message2/Response/CE//*[key('map', concat(ancestor::CE/id, '|', node-name(.), '|', count((., preceding-sibling::*[node-name(.) = node-name(current())]))))]">
<xsl:copy-of select="key('map', concat(ancestor::CE/id, '|', node-name(.), '|', count((., preceding-sibling::*[node-name(.) = node-name(current())]))))"/>
</xsl:template>
</xsl:transform>
但希望能解决 http://xsltransform.hikmatu.com/pPgCcoA 中的问题,即使是旧版本的 Saxon。