如何使用平面xml作为输入来构建逻辑层次树..?

时间:2017-10-24 19:52:51

标签: xml xslt

<root>
    <food>
        <id>fruits</id>
        <parent />
        <level>1</level>
    </food>
    <food>
        <id>sourFruits</id>
        <parent>fruits</parent>
        <level>2</level>
    </food>
    <food>
        <id>sweetFruits</id>
        <parent>fruits</parent>
        <level>2</level>
    </food>
    <food>
        <id>lemon</id>
        <parent>sourFruits</parent>
        <level>3</level>
    </food>
    <food>
        <id>grapes</id>
        <parent>sweetFruits</parent>
        <level>3</level>
    </food>
    <food>
        <id>oranges</id>
        <parent>sweetFruits</parent>
        <level>3</level>
    </food>
</root>

level标记表示层次结构(要形成的逻辑树的节点级别)。 我需要将这个xml(上面)转换为另一个xml,如下所示。 图片如下: - 水果是根本要素。 水果有两个孩子sourFruits和sweetFruits。 sourFruits有一个孩子柠檬和sweetFruits有橙子和葡萄作为孩子。

此时我真的很困惑......

<goodFoods>
    <foodType>
        <name>fruits</name>
        <taste>
            <tasteType>sourFruits</tasteType>
            <fruit>
                <fruitname>lemon</fruitname>
            </fruit>
        </taste>
        <taste>
            <tasteType>sweetFruits</tasteType>
            <fruit>
                <fruitname>grapes</fruitname>
            </fruit>
            <fruit>
                <fruitname>oranges</fruitname>
            </fruit>
        </taste>
    </foodType>
</goodFoods>

2 个答案:

答案 0 :(得分:0)

您希望通过foot及其父级引用level元素,使用XSLT,您可以使用密钥,在XSLT 3.0中使用复合键

<xsl:key name="children" match="food" composite="yes" use="xs:integer(level), string(parent)"/>

很好,对于早期版本的XSLT,您需要使用单个键值,例如将级别和父级连接起来的字符串,例如。

<xsl:key name="children" match="food" use="concat(level, '|', parent)"/>

然后,只需要将每个级别转换为目标格式,并使用XSLT 3.0找到具有密钥的子项

<xsl:template match="root">
    <goodFoods>
        <xsl:apply-templates select="key('children', (1, ''))"/>
    </goodFoods>
</xsl:template>

<xsl:template match="food[level = 1]">
    <foodType>
        <name>{id}</name>
        <xsl:apply-templates select="key('children', (level + 1, string(id)))"/>
    </foodType>     
</xsl:template>

<xsl:template match="food[level = 2]">
    <taste>
        <tasteType>{id}</tasteType>
        <xsl:apply-templates select="key('children', (level + 1, string(id)))"/>
    </taste>
</xsl:template>

<xsl:template match="food[level = 3]">
    <fruit>
        <fruitName>{id}</fruitName>
    </fruit>
</xsl:template>

使用XSLT 3的完整样式表是

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    expand-text="yes"
    version="3.0">

    <xsl:key name="children" match="food" composite="yes" use="xs:integer(level), string(parent)"/>

    <xsl:output indent="yes"/>

    <xsl:template match="root">
        <goodFoods>
            <xsl:apply-templates select="key('children', (1, ''))"/>
        </goodFoods>
    </xsl:template>

    <xsl:template match="food[level = 1]">
        <foodType>
            <name>{id}</name>
            <xsl:apply-templates select="key('children', (level + 1, string(id)))"/>
        </foodType>     
    </xsl:template>

    <xsl:template match="food[level = 2]">
        <taste>
            <tasteType>{id}</tasteType>
            <xsl:apply-templates select="key('children', (level + 1, string(id)))"/>
        </taste>
    </xsl:template>

    <xsl:template match="food[level = 3]">
        <fruit>
            <fruitName>{id}</fruitName>
        </fruit>
    </xsl:template>

</xsl:stylesheet>

使用XSLT 1或2键,您需要稍微调整模板,例如

    <xsl:template match="food[level = 1]">
        <foodType>
            <name>
               <xsl:value-of select="id"/>
            </name>
            <xsl:apply-templates select="key('children', concat(level + 1, '|', id))"/>
        </foodType>     
    </xsl:template>

应该清楚如何调整其他模板。

答案 1 :(得分:0)

不确定XSLT,但是这里是如何在xsh中完成的:

open file.xml ;
for &{ sort :n :d :k level /root/food[parent!=""] } {
    my $f = . ;
    mv $f into ../food[id=$f/parent] ;
}

rename goodFoods /root ;

rename fruit    //food[level = 3] ;
rename taste    //food[level = 2] ;
rename foodType //food[level = 1] ;

rename fruitname //fruit/id ;
rename tasteType //taste/id ;
rename name      //foodType/id ;

remove ( //level | //parent ) ;

save :b ;

使用xmllint --format处理后,输出为

<goodFoods>
  <foodType>
    <name>fruits</name>
    <taste>
      <tasteType>sourFruits</tasteType>
      <fruit>
        <fruitname>lemon</fruitname>
      </fruit>
    </taste>
    <taste>
      <tasteType>sweetFruits</tasteType>
      <fruit>
        <fruitname>grapes</fruitname>
      </fruit>
      <fruit>
        <fruitname>oranges</fruitname>
      </fruit>
    </taste>
  </foodType>
</goodFoods>