[英]Merge xml contents of elements with same parent attribute values using xslt 2.0
I have two xml files我有两个 xml 文件
file1.xml文件1.xml
<?xml version="1.0" encoding="UTF-8"?>
<tv>
...
<programme start="20200814040000 +0000" stop="20200814050000 +0000" channel="A">
<title>A</title>
<sub-title>C</sub-title>
<desc>F</desc>
</programme>
...
<programme start="20200814090000 +0000" stop="20200814093000 +0000" channel="A">
<title>B</title>
<sub-title>D</sub-title>
<desc>E</desc>
</programme>
...
</tv>
file2.xml文件2.xml
<?xml version="1.0" encoding="UTF-8"?>
<tv>
...
<programme start="20200814040000 +0000" stop="20200814050000 +0000" channel="A">
<title>G</title>
<sub-title>C</sub-title>
<desc>H</desc>
<episode-num system="onscreen">S9 E13</episode-num>
</programme>
...
<programme start="20200814090000 +0000" stop="20200814093000 +0000" channel="A">
<title>K</title>
<sub-title>L</sub-title>
<desc>M</desc>
<episode-num system="onscreen">S3 E2</episode-num>
</programme>
...
</tv>
I would like an xslt 2 template to get a new file我想要一个 xslt 2 模板来获取新文件
file3.xml文件3.xml
<?xml version="1.0" encoding="UTF-8"?>
<tv>
...
<programme start="20200814040000 +0000" stop="20200814050000 +0000" channel="A">
<title>A (G)</title>
<sub-title>C</sub-title>
<desc>F (H)</desc>
<episode-num system="onscreen">S9 E13</episode-num>
</programme>
...
<programme start="20200814090000 +0000" stop="20200814093000 +0000" channel="A">
<title>B (K)</title>
<sub-title>D (L)</sub-title>
<desc>E (M)</desc>
<episode-num system="onscreen">S3 E2</episode-num>
</programme>
...
</tv>
I experimented a little bit, but I couldn't get the expected output.我做了一点实验,但我无法得到预期的 output。 Any help would be appreciated.
任何帮助,将不胜感激。
Edited for precision为精确而编辑
when programme attributes are the same from each file:当每个文件的程序属性相同时:
In XSLT 3 perhaps the function for-each-pair
can help:在 XSLT 3
for-each-pair
的 function 可能会有所帮助:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:param name="doc2">
<tv>
<channel id="Discovery">
<display-name lang="el">Discovery</display-name>
</channel>
<programme start="20200814040000 +0000" stop="20200814050000 +0000" channel="Discovery">
<title lang="el">Wheeler Dealers</title>
<sub-title lang="el">BMW Isetta</sub-title>
<desc lang="el">Mike tracks down an Isetta Bubble. </desc>
<episode-num system="onscreen">S9 E13</episode-num>
</programme>
</tv>
</xsl:param>
<xsl:output indent="yes"/>
<xsl:function name="mf:merge-pair">
<xsl:param name="programme1"/>
<xsl:param name="programme2"/>
<xsl:if test="deep-equal($programme1/@*, $programme2/@*)">
<xsl:copy select="$programme1">
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="$programme1/*, $programme2/*" composite="yes" group-by="node-name(), @*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:value-of select="head(current-group()), tail(current-group()) ! ('(' || . || ')')"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:if>
</xsl:function>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="tv">
<xsl:copy>
<xsl:apply-templates select="@*, channel"/>
<xsl:sequence
select="for-each-pair(programme, $doc2/tv/programme, mf:merge-pair#2)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In above example I have inlined the second document for completeness and self-containedness but of course in a real life application you can use eg <xsl:param name="doc2" select="doc('input2.xml')"/>
.在上面的示例中,为了完整性和自包含性,我已内联第二个文档,但当然在现实生活中的应用程序中,您可以使用例如
<xsl:param name="doc2" select="doc('input2.xml')"/>
.
XSLT 3 with for-each-pair is available with Saxon 10 all editions or the commercial 9.8 or 9.9 editions of Saxon or in Saxon-JS 2 for Node.js or in the browser.带有 for-each-pair 的 XSLT 3 可用于 Saxon 10 所有版本或 Saxon 的商业 9.8 或 9.9 版本或用于 Node.js 或浏览器的 Saxon-JS 2。
As for your comment, it seems you have edited the samples and now it appears that duplicated contents like BMW Isetta (BMW Isetta)
is supposed to be eliminated so you could change至于您的评论,您似乎已经编辑了示例,现在看来像
BMW Isetta (BMW Isetta)
这样的重复内容应该被删除,因此您可以更改
<xsl:value-of select="head(current-group()), tail(current-group()) ! ('(' || . || ')')"/>
to至
<xsl:value-of select="let $values := distinct-values(current-group()) return (head(
$values), tail($values)! ('(' || . || ')'))"/>
Output for me with your edited samples and Saxon HE 10.1 is Output 对我来说,你编辑的样本和撒克逊 HE 10.1 是
<tv>
<programme start="20200814040000 +0000"
stop="20200814050000 +0000"
channel="A">
<title>A (G)</title>
<sub-title>C</sub-title>
<desc>F (H)</desc>
<episode-num system="onscreen">S9 E13</episode-num>
</programme>
<programme start="20200814090000 +0000"
stop="20200814093000 +0000"
channel="A">
<title>B (K)</title>
<sub-title>D (L)</sub-title>
<desc>E (M)</desc>
<episode-num system="onscreen">S3 E2</episode-num>
</programme>
</tv>
Complete stylesheet is完整的样式表是
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:param name="doc2" select="doc('file2.xml')"/>
<xsl:output indent="yes"/>
<xsl:function name="mf:merge-pair">
<xsl:param name="programme1"/>
<xsl:param name="programme2"/>
<xsl:if test="deep-equal($programme1/@*, $programme2/@*)">
<xsl:copy select="$programme1">
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="$programme1/*, $programme2/*" composite="yes" group-by="node-name(), @*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:value-of select="let $values := distinct-values(current-group()) return (head(
$values), tail($values)! ('(' || . || ')'))"/>
</xsl:copy>
</xsl:for-each-group>
</xsl:copy>
</xsl:if>
</xsl:function>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="tv">
<xsl:copy>
<xsl:apply-templates select="@*, channel"/>
<xsl:sequence
select="for-each-pair(programme, $doc2/tv/programme, mf:merge-pair#2)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I would do something like:我会做类似的事情:
<xsl:variable name="file1" select="doc('file1.xml')"/>
<xsl:variable name="file2" select="doc('file2.xml')"/>
<xsl:template name="xsl:initial-template">
<tv>
<xsl:copy-of select="$file1/tv/channel"/>
<xsl:for-each-group select="($file1|file2)/tv/programme"
group-by="@stop, @start, @channel" composite="yes">
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:element name="{name()}">
<xsl:copy-of select="current-group()/@*"/>
<xsl:value-of select="current-group()[1]"/>
<xsl:for-each select="current-group()[2]">
<xsl:value-of select="'(', ., ')'"/>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</xsl:for-each-group>
</tv>
</xsl:template>
Not tested.未测试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.