将 N 列 XML 展开为树结构
Unflatten N-Columns XML to tree structure
我在创建 XSLT 时遇到问题,它会使该结构变平:
<RS>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>D</C3>
<C4>1</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>E</C3>
<C4>2</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>F</C2>
<C3></C3>
<C4>3</C4>
</R>
</RS>
或者那个结构:
<RS>
<R>
<C0>A->B->C->D</C0>
<C1>1</C1>
</R>
<R>
<C0>A->B->C->E</C0>
<C1>2</C1>
</R>
<R>
<C0>A->B->F</C0>
<C1>3</C1>
</R>
</RS>
进入这个 XML 嵌套树:
<A>
<B>
<C>
<D>1</D>
<E>2</E>
</C>
<F>3</F>
</B>
</A>
换句话说:我想将 1-N 列(或者:第一列 '->' 分隔字符串值)作为路径并将其转换为嵌套的 XML 节点,最后一列作为节点值。
我已经挣扎了一个多星期了,开始享受从 XML 到 JSON 的转变。
使用 XSLT 3(略有改动)输入
<RS>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>D</C3>
<C4>1</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>E</C3>
<C4>2</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>F</C2>
<C4>3</C4>
</R>
</RS>
可以像
那样用递归分组进行转换
<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:function name="mf:group" as="node()*">
<xsl:param name="rows" as="element(R)*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="*[$index]">
<xsl:try>
<xsl:element name="{current-grouping-key()}">
<xsl:sequence select="mf:group(current-group(), $index + 1)"/>
</xsl:element>
<xsl:catch>
<xsl:value-of select="current-grouping-key()"/>
</xsl:catch>
</xsl:try>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="RS">
<xsl:sequence select="mf:group(R, 1)"/>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
</xsl:stylesheet>
进入
<A>
<B>
<C>
<D>1</D>
<E>2</E>
</C>
<F>3</F>
</B>
</A>
替代输入结构也可以在 XSLT 3 中使用递归分组进行转换,这次使用映射作为中间数据结构:
<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:function name="mf:group" as="node()*">
<xsl:param name="rows" as="map(*)*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="?columns[$index]">
<xsl:element name="{current-grouping-key()}">
<xsl:sequence select="if (exists(?columns[$index + 1])) then mf:group(current-group(), $index + 1) else ?value"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="RS">
<xsl:sequence select="mf:group(R ! map { 'columns' : tokenize(C0, '->'), 'value' : data(C1) }, 1)"/>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
</xsl:stylesheet>
使用地图的算法可以通过使用
适应您的第一个输入结构
<xsl:function name="mf:group" as="node()*">
<xsl:param name="rows" as="map(*)*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="?columns[$index]">
<xsl:element name="{current-grouping-key()}">
<xsl:sequence select="if (exists(?columns[$index + 1])) then mf:group(current-group(), $index + 1) else ?value"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="RS">
<xsl:sequence select="mf:group(R ! map { 'columns' : *[. castable as xs:NCName], 'value' : data(*[last()]) }, 1)"/>
</xsl:template>
我在创建 XSLT 时遇到问题,它会使该结构变平:
<RS>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>D</C3>
<C4>1</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>E</C3>
<C4>2</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>F</C2>
<C3></C3>
<C4>3</C4>
</R>
</RS>
或者那个结构:
<RS>
<R>
<C0>A->B->C->D</C0>
<C1>1</C1>
</R>
<R>
<C0>A->B->C->E</C0>
<C1>2</C1>
</R>
<R>
<C0>A->B->F</C0>
<C1>3</C1>
</R>
</RS>
进入这个 XML 嵌套树:
<A>
<B>
<C>
<D>1</D>
<E>2</E>
</C>
<F>3</F>
</B>
</A>
换句话说:我想将 1-N 列(或者:第一列 '->' 分隔字符串值)作为路径并将其转换为嵌套的 XML 节点,最后一列作为节点值。
我已经挣扎了一个多星期了,开始享受从 XML 到 JSON 的转变。
使用 XSLT 3(略有改动)输入
<RS>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>D</C3>
<C4>1</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>C</C2>
<C3>E</C3>
<C4>2</C4>
</R>
<R>
<C0>A</C0>
<C1>B</C1>
<C2>F</C2>
<C4>3</C4>
</R>
</RS>
可以像
那样用递归分组进行转换<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:function name="mf:group" as="node()*">
<xsl:param name="rows" as="element(R)*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="*[$index]">
<xsl:try>
<xsl:element name="{current-grouping-key()}">
<xsl:sequence select="mf:group(current-group(), $index + 1)"/>
</xsl:element>
<xsl:catch>
<xsl:value-of select="current-grouping-key()"/>
</xsl:catch>
</xsl:try>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="RS">
<xsl:sequence select="mf:group(R, 1)"/>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
</xsl:stylesheet>
进入
<A>
<B>
<C>
<D>1</D>
<E>2</E>
</C>
<F>3</F>
</B>
</A>
替代输入结构也可以在 XSLT 3 中使用递归分组进行转换,这次使用映射作为中间数据结构:
<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:function name="mf:group" as="node()*">
<xsl:param name="rows" as="map(*)*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="?columns[$index]">
<xsl:element name="{current-grouping-key()}">
<xsl:sequence select="if (exists(?columns[$index + 1])) then mf:group(current-group(), $index + 1) else ?value"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="RS">
<xsl:sequence select="mf:group(R ! map { 'columns' : tokenize(C0, '->'), 'value' : data(C1) }, 1)"/>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
</xsl:stylesheet>
使用地图的算法可以通过使用
适应您的第一个输入结构 <xsl:function name="mf:group" as="node()*">
<xsl:param name="rows" as="map(*)*"/>
<xsl:param name="index" as="xs:integer"/>
<xsl:for-each-group select="$rows" group-by="?columns[$index]">
<xsl:element name="{current-grouping-key()}">
<xsl:sequence select="if (exists(?columns[$index + 1])) then mf:group(current-group(), $index + 1) else ?value"/>
</xsl:element>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="RS">
<xsl:sequence select="mf:group(R ! map { 'columns' : *[. castable as xs:NCName], 'value' : data(*[last()]) }, 1)"/>
</xsl:template>