使用 XSLT 扩展带有路径定界符的扁平 XML
Using XSLT to expand a flattened XML with path delimiters
(编辑)根据@michael.hor257k 的建议,我还包含了输入的详细形式 XML。
我有一个我无法控制的输入 XML,需要在 XSLT 中对其进行转换。请注意,与SO上的许多“相关答案”不同,在这种情况下,“深度”级别不是每个项目的属性,而是需要从路径计算。
这是我的问题的 XSLT fiddle:http://xsltransform.net/6rexjhn/3
这是输入的简化形式(紧凑形式):
<?xml version="1.0" encoding="UTF-8"?>
<data>
<settings>
<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">A</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="attribute" level="one_six.size" target="six.size" >large</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>
这里是详细的形式:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<settings>
<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">A</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>
展平是这样的:(1) 下划线代表目标的子元素,(2) 句点代表属性,(3) 最大深度未知但应该是合理的,(4)有子元素的元素只有子元素,没有独立值。这就是我想要得到的:
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ac="http://www.example.org/Standards/abc/1"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two>A</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>
基于 this 所以答案也没有深度属性,这是我尝试过的(使用 XSLT 1.0)。很多数据都丢失了,我不知道如何处理属性。
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" doctype-public="XSLT-compat" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="siblings" match="*[not(self::field)]" use="generate-id(preceding-sibling::field[1])" />
<xsl:key name="nextlevel" match="field" use="generate-id(preceding-sibling::field[@level][starts-with(current(), concat(., '_'))][1])" />
<xsl:template match="/">
<template xmlns="http://www.example.org/standards/template/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ac="http://www.example.org/Standards/abc/1"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="{//field[@target='one.quality']}" weight="{//field[@target='one.weight']}">
<xsl:apply-templates select="//field[@level='one']" />
</one>
</template>
</xsl:template>
<!-- Fetch elements -->
<xsl:template match="//field[@style='element']">
<xsl:element name="{@target}">
<xsl:apply-templates select="key('siblings', generate-id())" />
<xsl:apply-templates select="key('nextlevel', generate-id())" />
</xsl:element>
</xsl:template>
<!-- identity copy transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
这就是我使用上面的 XSLT 得到的结果:
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ac="http://www.example.org/Standards/abc/1"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg"/>
</template>
在我看来,鉴于您的“详细”输入,您可以使用以下方法轻松实现预期结果:
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: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: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:template>
<xsl:template match="field[@style='attribute']">
<xsl:attribute name="{substring-after(@target, '.')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
注意没有hard-coding个节点名,层级深度无限制
(编辑)根据@michael.hor257k 的建议,我还包含了输入的详细形式 XML。
我有一个我无法控制的输入 XML,需要在 XSLT 中对其进行转换。请注意,与SO上的许多“相关答案”不同,在这种情况下,“深度”级别不是每个项目的属性,而是需要从路径计算。
这是我的问题的 XSLT fiddle:http://xsltransform.net/6rexjhn/3
这是输入的简化形式(紧凑形式):
<?xml version="1.0" encoding="UTF-8"?>
<data>
<settings>
<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">A</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="attribute" level="one_six.size" target="six.size" >large</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>
这里是详细的形式:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<settings>
<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">A</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>
展平是这样的:(1) 下划线代表目标的子元素,(2) 句点代表属性,(3) 最大深度未知但应该是合理的,(4)有子元素的元素只有子元素,没有独立值。这就是我想要得到的:
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ac="http://www.example.org/Standards/abc/1"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg">
<two>A</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>
基于 this 所以答案也没有深度属性,这是我尝试过的(使用 XSLT 1.0)。很多数据都丢失了,我不知道如何处理属性。
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" doctype-public="XSLT-compat" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="siblings" match="*[not(self::field)]" use="generate-id(preceding-sibling::field[1])" />
<xsl:key name="nextlevel" match="field" use="generate-id(preceding-sibling::field[@level][starts-with(current(), concat(., '_'))][1])" />
<xsl:template match="/">
<template xmlns="http://www.example.org/standards/template/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ac="http://www.example.org/Standards/abc/1"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="{//field[@target='one.quality']}" weight="{//field[@target='one.weight']}">
<xsl:apply-templates select="//field[@level='one']" />
</one>
</template>
</xsl:template>
<!-- Fetch elements -->
<xsl:template match="//field[@style='element']">
<xsl:element name="{@target}">
<xsl:apply-templates select="key('siblings', generate-id())" />
<xsl:apply-templates select="key('nextlevel', generate-id())" />
</xsl:element>
</xsl:template>
<!-- identity copy transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
这就是我使用上面的 XSLT 得到的结果:
<?xml version="1.0" encoding="UTF-8"?>
<template xmlns="http://www.example.org/standards/template/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ac="http://www.example.org/Standards/abc/1"
xsi:schemaLocation="http://www.example.org/standards/template.xsd"
Version="2022-01">
<one quality="high" weight="10 kg"/>
</template>
在我看来,鉴于您的“详细”输入,您可以使用以下方法轻松实现预期结果:
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: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: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:template>
<xsl:template match="field[@style='attribute']">
<xsl:attribute name="{substring-after(@target, '.')}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
注意没有hard-coding个节点名,层级深度无限制