【问题标题】:Unflatten N-Columns XML to tree structure将 N 列 XML 展开为树结构
【发布时间】:2021-07-07 02:47:28
【问题描述】:

我在创建 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 的转换。

【问题讨论】:

  • 1.如果没有其他输入,这个问题就够难了。选择其中一个选项,如果解决方案不涵盖另一个选项,请发布一个新问题。 2. 不清楚示例中给出了什么,什么只是一个示例 - 例如C0, C1 ... 元素的名称是提前知道的吗?如果其中之一的值不是有效的元素名称怎么办? 3. 您的处理器支持哪个 XSLT 版本?
  • 您好,感谢您的回复。是的,结构会提前知道,XML 输入是来自报告工具的 XML Analysys Rowset ()。我假设 XSLT 2.0 将是 PROD 上的最高版本。但我的安全猜测是最终将是 1.0(不要问为什么,这个客户端就是这样工作的 :)),所以欢迎所有替代方案 :)

标签: xml xslt tree transformation


【解决方案1】:

使用 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>

【讨论】:

    猜你喜欢
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 1970-01-01
    相关资源
    最近更新 更多