XSLT中的组输出但只显示唯一标题?

时间:2018-04-18 15:57:27

标签: xml xslt xslt-1.0

我有一个XML文档,内容与

类似
<?xml version="1.0" encoding="UTF-8"?>
<Mod>
<Input1>
    <Name>BackInput</Name>
    <Transform>
        <Subsystem>Transmission</Subsystem>
    </Transform>
</Input1>
<Input2>
    <Name>NeutralInput</Name>
    <Transform>
        <Subsystem>Transmission</Subsystem>
    </Transform>
</Input2>
<Input3>
    <Name>LightingInput</Name>
    <Transform>
        <Subsystem>Lighting</Subsystem>
    </Transform>
</Input3>
<Output1>
    <Name>BackOutput</Name>
    <Transform>
        <Subsystem>Transmission</Subsystem>
    </Transform>
</Output1>
<Output2>
    <Name>NeutralOutput</Name>
    <Transform>
        <Subsystem>Transmission</Subsystem>
    </Transform>
</Output2>
<Output3>
    <Name>LightingOutput</Name>
    <Transform>
        <Subsystem>Lighting</Subsystem>
    </Transform>
</Output3>
<VariableData>
    <Threshold name="LightingMax">
        <Component>Lighting</Component>
    </Threshold>
</VariableData>
</Mod>

我想获得唯一的子系统,以及所有唯一的前面的Name文本。按子系统排序,最后按名称排序。预期输出为

Lighting
  LightingInput
  LightingOutput
Transmission
  BackInput
  BackOutput
  DriveInput
  DriveOutput
  NeutralInput
  NeutralOutput

这是模拟数据。我似乎无法理解如何只输出唯一的数据项。

这是我现在使用的XSLT。请随意评论XSLT的任何方面,因为这是我第一次使用它。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="newline" select="'&#10;'"/>
<xsl:variable name="tab" select="'&#9;'"/>
<xsl:template match="/">
    <xsl:copy>
        <xsl:apply-templates select="//Subsystem|//Component">
            <xsl:sort select="."/>
            <xsl:sort select="../@name"/>
            <xsl:sort select="../preceding-sibling::Name"/>
        </xsl:apply-templates>
    </xsl:copy>
</xsl:template>

<xsl:template match="//Subsystem|//Component">
    <xsl:value-of select="concat($newline, .)"/>
    <xsl:if test="../preceding-sibling::Name">
        <xsl:value-of select="concat($newline, $tab, ../preceding-sibling::Name)"/>
    </xsl:if>
    <xsl:if test="../@name">
        <xsl:value-of select="concat($newline, $tab, ../@name)"/>
    </xsl:if>
</xsl:template>

</xsl:stylesheet>

当上面的XSLT应用于XML时的输出(注意,我在使用它时在第一行得到一个换行符,它似乎是因为我的XSLT但是如果我移动第一个xsl:value-of我不喜欢#39;得到我期待的输出

<newline>
Lighting
    LightingInput
Lighting
    LightingOutput
Lighting
    LightingMax
Transmission
    BackInput
Transmission
    BackOutput
Transmission
    NeutralInput
Transmission
    NeutralOutput

2 个答案:

答案 0 :(得分:1)

  

随意评论XSLT的任何方面,因为这是第一个   我正在使用它。

以下是对现有样式表的一些评论...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:variable name="newline" select="'&#10;'"/>
  <xsl:variable name="tab" select="'&#9;'"/>

  <xsl:template match="/">
    <!--When your output method is text, you don't usually need to use xsl:copy.
    Also, using xsl:copy when the context is the document node doesn't do anything
    helpful.-->
    <xsl:copy>
      <xsl:apply-templates select="//Subsystem|//Component">
        <xsl:sort select="."/>
        <xsl:sort select="../@name"/>
        <xsl:sort select="../preceding-sibling::Name"/>
      </xsl:apply-templates>
    </xsl:copy>
  </xsl:template>

  <!--You don't need the "//" in the match pattern. Just use "Subsystem|Component".
  See https://www.w3.org/TR/xslt-10/#patterns for more info.-->
  <xsl:template match="//Subsystem|//Component">
    <!--This outputs a newline for every Subsystem or Component. Instead, just output
    a newline if the position() is greater than 1. That way you won't have an extra
    newline at the beginning of your output.-->
    <xsl:value-of select="concat($newline, .)"/>
    <xsl:if test="../preceding-sibling::Name">
      <xsl:value-of select="concat($newline, $tab, ../preceding-sibling::Name)"/>
    </xsl:if>
    <xsl:if test="../@name">
      <xsl:value-of select="concat($newline, $tab, ../@name)"/>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

由于您使用的是XSLT 1.0,我要做的就是使用Muenchian Grouping

首先按其值SubsystemComponent元素进行分组(示例中的第一个“level1”xsl:key)。您只是要输出此键中第一个节点的值。这将为您提供独特的清单。

然后将Name元素和name属性按ComponentSubsystem的值(示例中的第二个“名称”xsl:键)进行分组。获取组件或子系统有点棘手,因为您选择了元素或属性,并且它们位于树中的不同级别。要做到这一点,我们首先需要将树(..)返回到父级,然后将树(//)返回到组件或子系统。

请花一些时间查看上面链接的Muenchian Grouping page;它将帮助您理解我的示例中的分组部分。

示例...

XML输入

<Mod>
    <Input1>
        <Name>BackInput</Name>
        <Transform>
            <Subsystem>Transmission</Subsystem>
        </Transform>
    </Input1>
    <Input2>
        <Name>NeutralInput</Name>
        <Transform>
            <Subsystem>Transmission</Subsystem>
        </Transform>
    </Input2>
    <Input3>
        <Name>LightingInput</Name>
        <Transform>
            <Subsystem>Lighting</Subsystem>
        </Transform>
    </Input3>
    <Output1>
        <Name>BackOutput</Name>
        <Transform>
            <Subsystem>Transmission</Subsystem>
        </Transform>
    </Output1>
    <Output2>
        <Name>NeutralOutput</Name>
        <Transform>
            <Subsystem>Transmission</Subsystem>
        </Transform>
    </Output2>
    <Output3>
        <Name>LightingOutput</Name>
        <Transform>
            <Subsystem>Lighting</Subsystem>
        </Transform>
    </Output3>
    <VariableData>
        <Threshold name="LightingMax">
            <Component>Lighting</Component>
        </Threshold>
    </VariableData>
</Mod>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:key name="level1" 
    match="Subsystem|Component" 
    use="normalize-space()"/>
  <xsl:key name="names" 
    match="Name|*[@name]/@name" 
    use="normalize-space(..//*[self::Subsystem or self::Component])"/>

  <xsl:template match="/Mod">
    <xsl:for-each 
      select=".//*[self::Subsystem or self::Component][
      count(.|key('level1',normalize-space())[1])=1]">
      <xsl:sort select="normalize-space()"/>      
      <xsl:if test="position() > 1">
        <xsl:value-of select="'&#xA;'"/>
      </xsl:if>
      <xsl:value-of select="normalize-space()"/>
      <xsl:for-each select="key('names',normalize-space())">
        <xsl:sort select="normalize-space()"/>
        <xsl:value-of select="concat('&#xA;&#x9;',normalize-space())"/>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet>

<强>输出

Lighting
    LightingInput
    LightingMax
    LightingOutput
Transmission
    BackInput
    BackOutput
    NeutralInput
    NeutralOutput

小提琴:http://xsltfiddle.liberty-development.net/6qVRKvQ

答案 1 :(得分:0)

考虑Muenchian Grouping按XSLT 1.0中的特定值索引文档,并循环遍历相应的项目,例如祖父母名称:../../Name

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text" omit-xml-declaration="yes"/>

   <xsl:key name="subid" match="Subsystem" use="."/>

   <xsl:template match ="/Mod">
        <xsl:apply-templates select="descendant::Subsystem[generate-id() = 
                                           generate-id(key('subid', .)[1])]">
            <xsl:sort select="."/>
        </xsl:apply-templates>
   </xsl:template>

   <xsl:template match ="Subsystem">
        <xsl:value-of select="."/><xsl:text>&#xa;</xsl:text><!-- LINE BREAK -->

        <xsl:for-each select="key('subid', .)">
            <xsl:sort select="../../Name"/>
            <xsl:text>&#009;</xsl:text>            <!--    TAB     -->
            <xsl:value-of select="../../Name"/>
            <xsl:text>&#xa;</xsl:text>             <!-- LINE BREAK -->
        </xsl:for-each>
   </xsl:template>

</xsl:stylesheet>

XSLTransform.Net DEMO