使用 Microsoft XSLT 1.0 克隆节点,同时将平面扩展为分层 XML
Clone nodes while expanding flat to hierarchical XML using Microsoft XSLT 1.0
TL;DR:这是我的 Fiddle:https://xsltfiddle.liberty-development.net/6qtiBn6/2
扩展 的答案,我实际上还需要从单个节点创建一些重复字段,这些字段的值由分隔符“|”分隔。我确实收到了一个参数字段 count
来告诉我要制作的那个节点 副本的数量 。节点是one_two
.
<?xml version="1.0" encoding="UTF-8"?>
<data>
<settings>
<field style="element" level="count" target="param">3</field>
<field style="element" level="one" target="one"></field>
<field style="attribute" level="one.quality" target="one.quality">high</field>
<field style="attribute" level="one.weight" target="one.weight">10 kg</field>
<field style="element" level="one_two" target="two"></field>
<field style="attribute" level="one_two.type" target="two.type">a|b|c</field>
<field style="element" level="one_two_ten" target="ten">alpha|beta|gamma</field>
<field style="attribute" level="one_two_ten.type" target="ten.type">apple|ball|NULL</field>
<field style="attribute" level="one_two_ten.age" target="ten.age">baby|young|old</field>
<field style="element" level="one_three" target="three"></field>
<field style="attribute" level="one_three.color" target="three.color">black</field>
<field style="element" level="one_three_four" target="four" >B</field>
<field style="attribute" level="one_three_four.length" target="four.length">12 cm</field>
<field style="attribute" level="one_three_four.width" target="four.width"> 7 cm</field>
<field style="element" level="one_three_five" target="five" >C</field>
<field style="element" level="one_six" target="six" ></field>
<field style="attribute" level="one_six.size" target="six.size" >large</field>
<field style="element" level="one_six_seven" target="seven" ></field>
<field style="element" level="one_six_seven_eight" target="eight">D</field>
<field style="element" level="one_nine" target="nine">E</field>
</settings>
</data>
这是我拥有的 XSLT 1.0 代码:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.example.org/standards/template/1" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="elem" match="field[@style='element']" use="substring-before(@level, @target)" />
<xsl:key name="attr" match="field[@style='attribute']" use="substring-before(@level, '.')" />
<xsl:variable name="maxcount" select="//field[@level = 'count']"/>
<xsl:template match="/">
<template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/standards/template.xsd" xmlns:ac="http://www.example.org/Standards/abc/1" Version="2022-01">
<xsl:apply-templates select="key('elem', '')" />
</template>
</xsl:template>
<xsl:template match="field[@style='element']">
<xsl:choose>
<xsl:when test="@level = 'one_two'">
<xsl:call-template name="multiply">
<xsl:with-param name="maxCount" select="$maxcount" />
<xsl:with-param name="target" select="@target" />
<xsl:with-param name="level" select="@level" />
</xsl:call-template>
</xsl:when>
<xsl:when test="@target != 'param'">
<xsl:element name="{@target}">
<xsl:apply-templates select="key('attr', @level)" />
<xsl:value-of select="." />
<xsl:apply-templates select="key('elem', concat(@level, '_'))" />
</xsl:element>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
<xsl:template match="field[@style='attribute']">
<xsl:attribute name="{substring-after(@target, '.')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template name="multiply">
<xsl:param name="maxCount" />
<xsl:param name="target" />
<xsl:param name="level" />
<xsl:param name="i" select="1" />
<xsl:choose>
<xsl:when test="$i <= $maxCount">
<xsl:element name="{$target}">
<xsl:apply-templates select="key('attr', @level)" />
<xsl:value-of select="." />
<xsl:apply-templates select="key('elem', concat(@level, '_'))" />
</xsl:element>
<xsl:call-template name="multiply">
<xsl:with-param name="maxCount" select="$maxCount" />
<xsl:with-param name="target" select="@target" />
<xsl:with-param name="level" select="@level" />
<xsl:with-param name="i" select="$i+1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
这是我从上面得到的结果:
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:ac="http://www.example.org/Standards/abc/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two type="a|b|c">
<ten type="apple|ball|NULL" age="baby|young|old">alpha|beta|gamma</ten>
</two>
<two type="a|b|c">
<ten type="apple|ball|NULL" age="baby|young|old">alpha|beta|gamma</ten>
</two>
<two type="a|b|c">
<ten type="apple|ball|NULL" age="baby|young|old">alpha|beta|gamma</ten>
</two>
<three color="black">
<four length="12 cm" width=" 7 cm">B</four>
<five>C</five>
</three>
<six size="large">
<seven>
<eight>D</eight>
</seven>
</six>
<nine>E</nine>
</one>
</template>
我还没有完全做到。即 这就是我需要的: 注意 one_two
如何扩展为 3 个元素,即 count
) 并且子元素从分隔列表中分离出相应的值。此外,NULL 关键字意味着将跳过第二个重复项的属性(因此我可能想在那里应用替换 - 待定)。
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:ac="http://www.example.org/Standards/abc/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two type="a">
<ten type="apple" age="baby">alpha</ten>
</two>
<two type="b">
<ten type="ball" age="young">beta</ten>
</two>
<two type="c">
<ten age="old">gamma</ten>
</two>
<three color="black">
<four length="12 cm" width=" 7 cm">B</four>
<five>C</five>
</three>
<six size="large">
<seven>
<eight>D</eight>
</seven>
</six>
<nine>E</nine>
</one>
</template>
这里我只需要再做两件事:
(1) 我需要修改 multiply
模板,以便每个相乘的元素都为计数器 $i 选择 substring
的相关部分。即当 $1 = 1 时,我们选择第一部分,等等。考虑如何在循环中为计数器 i
应用 choose
,然后选择相应项目的子字符串。
(2) 请注意 ten
元素(在重复元素 <two type="c">
下)如何没有类型,因为它在提供的字符串中为 NULL。我想我需要一种 IF 语句。
你输入的格式很不方便。
另外,您添加的需求中有一个很奇怪的地方:根据您的描述,元素:
<field style="element" level="count" target="param">3</field>
表示输入中的一些元素需要在输出中复制3次——但它没有指定哪个一个.您告诉我们该指令指的是:
<field style="element" level="one_two" target="two"></field>
及其后代节点 - 但此信息不包含在输入本身中(也许可以从后代节点的 string-values 是分隔字符串,每个字符串有 3 个标记这一事实得出,但是将需要一个截然不同的过程)。
无论如何,利用这些外部知识,我认为您可以将上一个问题的答案修改为:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.example.org/standards/template/1" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="elem" match="field[@style='element'][@level!='count']" use="substring-before(@level, @target)" />
<xsl:key name="attr" match="field[@style='attribute']" use="substring-before(@level, '.')" />
<xsl:template match="/">
<template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/standards/template.xsd" xmlns:ac="http://www.example.org/Standards/abc/1" Version="2022-01">
<xsl:apply-templates select="key('elem', '')" />
</template>
</xsl:template>
<!-- create element -->
<xsl:template match="field[@style='element']">
<xsl:param name="N" select="1"/>
<xsl:element name="{@target}">
<!-- add attributes -->
<xsl:apply-templates select="key('attr', @level)" >
<xsl:with-param name="N" select="$N"/>
</xsl:apply-templates>
<!-- add text node -->
<xsl:call-template name="get-Nth-token">
<xsl:with-param name="N" select="$N"/>
<xsl:with-param name="list" select="."/>
</xsl:call-template>
<!-- add child elements -->
<xsl:apply-templates select="key('elem', concat(@level, '_'))" >
<xsl:with-param name="N" select="$N"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<!-- create attribute -->
<xsl:template match="field[@style='attribute']">
<xsl:param name="N" select="1"/>
<xsl:attribute name="{substring-after(@target, '.')}">
<!-- string-value -->
<xsl:call-template name="get-Nth-token">
<xsl:with-param name="N" select="$N"/>
<xsl:with-param name="list" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<!-- handle "special" element -->
<xsl:template match="field[@level='one_two']" name="special" priority="1">
<xsl:param name="count" select="../field[@level='count']"/>
<xsl:if test="$count > 0">
<xsl:call-template name="special">
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
<two>
<xsl:apply-templates select="key('attr', @level) | key('elem', concat(@level, '_'))" >
<xsl:with-param name="N" select="$count"/>
</xsl:apply-templates>
</two>
</xsl:if>
</xsl:template>
<!-- get Nth token -->
<xsl:template name="get-Nth-token">
<xsl:param name="list"/>
<xsl:param name="N"/>
<xsl:param name="delimiter" select="'|'"/>
<xsl:choose>
<xsl:when test="$N = 1">
<xsl:value-of select="substring-before(concat($list, $delimiter), $delimiter)"/>
</xsl:when>
<xsl:when test="contains($list, $delimiter) and $N > 1">
<!-- recursive call -->
<xsl:call-template name="get-Nth-token">
<xsl:with-param name="list" select="substring-after($list, $delimiter)"/>
<xsl:with-param name="N" select="$N - 1"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
应用于您的输入示例,这将产生:
结果
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:ac="http://www.example.org/Standards/abc/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two type="a">
<ten type="apple" age="baby">alpha</ten>
</two>
<two type="b">
<ten type="ball" age="young">beta</ten>
</two>
<two type="c">
<ten type="NULL" age="old">gamma</ten>
</two>
<three color="black">
<four length="12 cm" width=" 7 cm">B</four>
<five>C</five>
</three>
<six size="large">
<seven>
<eight>D</eight>
</seven>
</six>
<nine>E</nine>
</one>
</template>
我认为这是预期的结果,只有一个小例外。
为避免使用 "NULL"
的 string-value 创建属性或元素,首先将返回的标记放在变量中;然后仅在 $token != 'NULL'
.
时创建节点
TL;DR:这是我的 Fiddle:https://xsltfiddle.liberty-development.net/6qtiBn6/2
扩展 count
来告诉我要制作的那个节点 副本的数量 。节点是one_two
.
<?xml version="1.0" encoding="UTF-8"?>
<data>
<settings>
<field style="element" level="count" target="param">3</field>
<field style="element" level="one" target="one"></field>
<field style="attribute" level="one.quality" target="one.quality">high</field>
<field style="attribute" level="one.weight" target="one.weight">10 kg</field>
<field style="element" level="one_two" target="two"></field>
<field style="attribute" level="one_two.type" target="two.type">a|b|c</field>
<field style="element" level="one_two_ten" target="ten">alpha|beta|gamma</field>
<field style="attribute" level="one_two_ten.type" target="ten.type">apple|ball|NULL</field>
<field style="attribute" level="one_two_ten.age" target="ten.age">baby|young|old</field>
<field style="element" level="one_three" target="three"></field>
<field style="attribute" level="one_three.color" target="three.color">black</field>
<field style="element" level="one_three_four" target="four" >B</field>
<field style="attribute" level="one_three_four.length" target="four.length">12 cm</field>
<field style="attribute" level="one_three_four.width" target="four.width"> 7 cm</field>
<field style="element" level="one_three_five" target="five" >C</field>
<field style="element" level="one_six" target="six" ></field>
<field style="attribute" level="one_six.size" target="six.size" >large</field>
<field style="element" level="one_six_seven" target="seven" ></field>
<field style="element" level="one_six_seven_eight" target="eight">D</field>
<field style="element" level="one_nine" target="nine">E</field>
</settings>
</data>
这是我拥有的 XSLT 1.0 代码:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.example.org/standards/template/1" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="elem" match="field[@style='element']" use="substring-before(@level, @target)" />
<xsl:key name="attr" match="field[@style='attribute']" use="substring-before(@level, '.')" />
<xsl:variable name="maxcount" select="//field[@level = 'count']"/>
<xsl:template match="/">
<template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/standards/template.xsd" xmlns:ac="http://www.example.org/Standards/abc/1" Version="2022-01">
<xsl:apply-templates select="key('elem', '')" />
</template>
</xsl:template>
<xsl:template match="field[@style='element']">
<xsl:choose>
<xsl:when test="@level = 'one_two'">
<xsl:call-template name="multiply">
<xsl:with-param name="maxCount" select="$maxcount" />
<xsl:with-param name="target" select="@target" />
<xsl:with-param name="level" select="@level" />
</xsl:call-template>
</xsl:when>
<xsl:when test="@target != 'param'">
<xsl:element name="{@target}">
<xsl:apply-templates select="key('attr', @level)" />
<xsl:value-of select="." />
<xsl:apply-templates select="key('elem', concat(@level, '_'))" />
</xsl:element>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
<xsl:template match="field[@style='attribute']">
<xsl:attribute name="{substring-after(@target, '.')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template name="multiply">
<xsl:param name="maxCount" />
<xsl:param name="target" />
<xsl:param name="level" />
<xsl:param name="i" select="1" />
<xsl:choose>
<xsl:when test="$i <= $maxCount">
<xsl:element name="{$target}">
<xsl:apply-templates select="key('attr', @level)" />
<xsl:value-of select="." />
<xsl:apply-templates select="key('elem', concat(@level, '_'))" />
</xsl:element>
<xsl:call-template name="multiply">
<xsl:with-param name="maxCount" select="$maxCount" />
<xsl:with-param name="target" select="@target" />
<xsl:with-param name="level" select="@level" />
<xsl:with-param name="i" select="$i+1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
这是我从上面得到的结果:
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:ac="http://www.example.org/Standards/abc/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two type="a|b|c">
<ten type="apple|ball|NULL" age="baby|young|old">alpha|beta|gamma</ten>
</two>
<two type="a|b|c">
<ten type="apple|ball|NULL" age="baby|young|old">alpha|beta|gamma</ten>
</two>
<two type="a|b|c">
<ten type="apple|ball|NULL" age="baby|young|old">alpha|beta|gamma</ten>
</two>
<three color="black">
<four length="12 cm" width=" 7 cm">B</four>
<five>C</five>
</three>
<six size="large">
<seven>
<eight>D</eight>
</seven>
</six>
<nine>E</nine>
</one>
</template>
我还没有完全做到。即 这就是我需要的: 注意 one_two
如何扩展为 3 个元素,即 count
) 并且子元素从分隔列表中分离出相应的值。此外,NULL 关键字意味着将跳过第二个重复项的属性(因此我可能想在那里应用替换 - 待定)。
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:ac="http://www.example.org/Standards/abc/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two type="a">
<ten type="apple" age="baby">alpha</ten>
</two>
<two type="b">
<ten type="ball" age="young">beta</ten>
</two>
<two type="c">
<ten age="old">gamma</ten>
</two>
<three color="black">
<four length="12 cm" width=" 7 cm">B</four>
<five>C</five>
</three>
<six size="large">
<seven>
<eight>D</eight>
</seven>
</six>
<nine>E</nine>
</one>
</template>
这里我只需要再做两件事:
(1) 我需要修改 multiply
模板,以便每个相乘的元素都为计数器 $i 选择 substring
的相关部分。即当 $1 = 1 时,我们选择第一部分,等等。考虑如何在循环中为计数器 i
应用 choose
,然后选择相应项目的子字符串。
(2) 请注意 ten
元素(在重复元素 <two type="c">
下)如何没有类型,因为它在提供的字符串中为 NULL。我想我需要一种 IF 语句。
你输入的格式很不方便。
另外,您添加的需求中有一个很奇怪的地方:根据您的描述,元素:
<field style="element" level="count" target="param">3</field>
表示输入中的一些元素需要在输出中复制3次——但它没有指定哪个一个.您告诉我们该指令指的是:
<field style="element" level="one_two" target="two"></field>
及其后代节点 - 但此信息不包含在输入本身中(也许可以从后代节点的 string-values 是分隔字符串,每个字符串有 3 个标记这一事实得出,但是将需要一个截然不同的过程)。
无论如何,利用这些外部知识,我认为您可以将上一个问题的答案修改为:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.example.org/standards/template/1" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="elem" match="field[@style='element'][@level!='count']" use="substring-before(@level, @target)" />
<xsl:key name="attr" match="field[@style='attribute']" use="substring-before(@level, '.')" />
<xsl:template match="/">
<template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.org/standards/template.xsd" xmlns:ac="http://www.example.org/Standards/abc/1" Version="2022-01">
<xsl:apply-templates select="key('elem', '')" />
</template>
</xsl:template>
<!-- create element -->
<xsl:template match="field[@style='element']">
<xsl:param name="N" select="1"/>
<xsl:element name="{@target}">
<!-- add attributes -->
<xsl:apply-templates select="key('attr', @level)" >
<xsl:with-param name="N" select="$N"/>
</xsl:apply-templates>
<!-- add text node -->
<xsl:call-template name="get-Nth-token">
<xsl:with-param name="N" select="$N"/>
<xsl:with-param name="list" select="."/>
</xsl:call-template>
<!-- add child elements -->
<xsl:apply-templates select="key('elem', concat(@level, '_'))" >
<xsl:with-param name="N" select="$N"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<!-- create attribute -->
<xsl:template match="field[@style='attribute']">
<xsl:param name="N" select="1"/>
<xsl:attribute name="{substring-after(@target, '.')}">
<!-- string-value -->
<xsl:call-template name="get-Nth-token">
<xsl:with-param name="N" select="$N"/>
<xsl:with-param name="list" select="."/>
</xsl:call-template>
</xsl:attribute>
</xsl:template>
<!-- handle "special" element -->
<xsl:template match="field[@level='one_two']" name="special" priority="1">
<xsl:param name="count" select="../field[@level='count']"/>
<xsl:if test="$count > 0">
<xsl:call-template name="special">
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
<two>
<xsl:apply-templates select="key('attr', @level) | key('elem', concat(@level, '_'))" >
<xsl:with-param name="N" select="$count"/>
</xsl:apply-templates>
</two>
</xsl:if>
</xsl:template>
<!-- get Nth token -->
<xsl:template name="get-Nth-token">
<xsl:param name="list"/>
<xsl:param name="N"/>
<xsl:param name="delimiter" select="'|'"/>
<xsl:choose>
<xsl:when test="$N = 1">
<xsl:value-of select="substring-before(concat($list, $delimiter), $delimiter)"/>
</xsl:when>
<xsl:when test="contains($list, $delimiter) and $N > 1">
<!-- recursive call -->
<xsl:call-template name="get-Nth-token">
<xsl:with-param name="list" select="substring-after($list, $delimiter)"/>
<xsl:with-param name="N" select="$N - 1"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
应用于您的输入示例,这将产生:
结果
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:ac="http://www.example.org/Standards/abc/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two type="a">
<ten type="apple" age="baby">alpha</ten>
</two>
<two type="b">
<ten type="ball" age="young">beta</ten>
</two>
<two type="c">
<ten type="NULL" age="old">gamma</ten>
</two>
<three color="black">
<four length="12 cm" width=" 7 cm">B</four>
<five>C</five>
</three>
<six size="large">
<seven>
<eight>D</eight>
</seven>
</six>
<nine>E</nine>
</one>
</template>
我认为这是预期的结果,只有一个小例外。
为避免使用 "NULL"
的 string-value 创建属性或元素,首先将返回的标记放在变量中;然后仅在 $token != 'NULL'
.