XSLT - 删除没有子节点匹配特定值的节点

时间:2014-01-29 13:34:39

标签: xml xslt xpath

我是XSLT的新手,我昨天开始阅读它并且我被卡住了。我有以下XML

<?xml version="1.0" encoding="utf-8" ?>
<Menu>
  <Function>
    <Name>ViewHome</Name>
    <Roles>
      <Role>Finance</Role>
      <Role>Advisor</Role>
      <Role>Admin</Role>
    </Roles>
  </Function>
  <Function>
    <Name>ViewStaff</Name>
    <ChildFunctions>
      <Function>
        <Name>StaffFunction1</Name>
        <Roles>
          <Role>Finance</Role>
        </Roles>
      </Function>
      <Function>
        <Name>StaffFunction2</Name>
        <Roles>
          <Role>Admin</Role>
        </Roles>
      </Function>
      <Function>
        <Name>StaffFunction3</Name>
        <Roles>
          <Role>Advisor</Role>
        </Roles>
      </Function>
    </ChildFunctions>
  </Function>
  <Function>
    <Name>ViewDiary</Name>
    <Roles>
      <Role>Advisor</Role>
    </Roles>
  </Function>
</Menu>

我希望转换为仅显示包含指定角色的那些函数(父级或子级)。因此,如果应用财务和顾问角色,则输出将如下:

<?xml version="1.0" encoding="utf-8" ?>
<Menu>
  <Function>
    <Name>ViewHome</Name>
    <Roles>
      <Role>Finance</Role>
      <Role>Advisor</Role>
    </Roles>
  </Function>
  <Function>
    <Name>ViewStaff</Name>
    <ChildFunctions>
      <Function>
        <Name>StaffFunction1</Name>
        <Roles>
          <Role>Finance</Role>
        </Roles>
      </Function>
      <Function>
        <Name>StaffFunction3</Name>
        <Roles>
          <Role>Advisor</Role>
        </Roles>
      </Function>
    </ChildFunctions>
  </Function>
  <Function>
    <Name>ViewDiary</Name>
    <Roles>
      <Role>Advisor</Role>
    </Roles>
  </Function>
</Menu>

我查看了此网站上的其他帖子,但无法生成所需的输出。我一直在使用的xsl是:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="xml" />

    <xsl:variable name="rolesList">Advisor Finance</xsl:variable>

    <xsl:template match="node()|@*">
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="Function[not(descendant::Role[contains($rolesList, .)])]"/>

<!--
    <xsl:template match="Function[not(descendant::Role='Finance')]"/>
-->

</xsl:stylesheet>

最终,rolesList变量将通过代码提供。我只是想让变换工作。如果我将注释掉的模板匹配用于“财务”角色,我会得到预期的结果。 非常感谢任何帮助。

4 个答案:

答案 0 :(得分:1)

使用XSLT 1.0使用字符串比较,您可以使用

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="xml" />

    <xsl:param name="roleList" select="'|Advisor|Finance|'"/>

    <xsl:template match="node()|@*" name="identity">
      <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="Function">
      <xsl:if test="descendant::Role[contains($roleList, concat('|', ., '|'))]">
         <xsl:call-template name="identity"/>
      </xsl:if>
    </xsl:template>

    <xsl:template match="Role">
      <xsl:if test="contains($roleList, concat('|', ., '|'))">
         <xsl:call-template name="identity"/>
      </xsl:if>
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

使用内部查找列表:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:my="your-urn-here">

  <my:rolesList>
    <my:role>Advisor</my:role>
    <my:role>Finance</my:role>
  </my:rolesList>


  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Function[not(descendant::Role = document('')/*/my:rolesList/my:role)]"
/>

</xsl:stylesheet>

答案 2 :(得分:0)

我怀疑如果有多个Role元素,则contains()无法正常工作,因为该函数不希望将列表作为其第二个参数。

请注意,您将角色列表提供为以空格分隔的列表。但是,如果没有Schema,则不会将其识别为列表类型。这就是为什么你必须依赖字符串函数,正如@ michael.hor257k所指出的那样。

这个(即包含变量的模板匹配)仅适用于XSLT 2.0,正如@Tim C所指出的那样。

使用以下样式表:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

   <xsl:output method="xml" indent="yes"/>
   <xsl:strip-space elements="*"/>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:variable name="roles">Finance Advisor</xsl:variable>

   <xsl:template match="Function[count(Roles/Role) = 1 and not(matches($roles,Roles/Role))]"/>

   <xsl:template match="Role[not(matches($roles,.))]"/>

</xsl:stylesheet>

<强>输出

<?xml version="1.0" encoding="UTF-8"?>
<Menu>
   <Function>
      <Name>ViewHome</Name>
      <Roles>
         <Role>Finance</Role>
         <Role>Advisor</Role>
      </Roles>
   </Function>
   <Function>
      <Name>ViewStaff</Name>
      <ChildFunctions>
         <Function>
            <Name>StaffFunction1</Name>
            <Roles>
               <Role>Finance</Role>
            </Roles>
         </Function>
         <Function>
            <Name>StaffFunction3</Name>
            <Roles>
               <Role>Advisor</Role>
            </Roles>
         </Function>
      </ChildFunctions>
   </Function>
   <Function>
      <Name>ViewDiary</Name>
      <Roles>
         <Role>Advisor</Role>
      </Roles>
   </Function>
</Menu>

答案 3 :(得分:0)

  

我希望转换为仅显示那些功能(父母或   child)包含指定的角色。

我相信你的样式表就是这样(虽然它不应该,但只有一些处理器 - 请参阅问题的评论以获取更多详细信息)。

然而,从已经通过测试的函数的后代角色列表中删除“其他”角色。为此,您需要添加另一个与Role匹配的模板。

  

最终,rolesList变量将通过代码

提供

理想情况下,列表将作为节点集(或可以转换为节点集的东西)提供,这样您就可以进行比较而无需求助于字符串函数。但是,在XSLT 2.0中,这方面有更多的灵活性。