XSLT,展平递归函数的结果

时间:2013-12-09 17:04:43

标签: xml xslt xpath recursion fix-protocol

我正在尝试删除未在xml文件中的已定义消息中直接或间接使用的节点。 “联系”如下定义。

我正在尝试处理(修复)xml文档以提取与我感兴趣的消息组件匹配的组件。 文档结构与

一致
<fix>
    <messages>
        <message name="myMessage1">
            <field name="abc"/>
            <component name="component1"/>
            <component name="component2"/>
        </message>
       <message name="myMessage2">
           <field name="hello"/>
           <component name="component1"/>
       </message>
    </messages>
    <components>
        <component name="component1">
        </component>
        <component name="component2">
            <group name="agroup">
                <field name="afield"/>
                <component name="component3"/>
            </group>
        </component>
        <component name="component3">
        </component>
        <component name="component4">
        </component>
        <component name="component5">
        </component>
        <component name="component6">
            <group name="agroup">
                <field name="afield"/>
                <component name="component3"/>
                <component name="component4"/>
            </group>
        </component>
    </components>
</fix>

所以我设法识别我所拥有的消息中的组。 我还能够递归地获取其他组件引用的组件。 我想展平返回的组件节点并将它们转换为一个唯一的集合,这是我认为我的方法失败的地方。

 <xsl:template match="components">
     <nodes>
         <xsl:for-each select="/fix/messages/message/component">
             <xsl:call-template name="findGroups">
                 <xsl:with-param name="groups" select="@name"/>
             </xsl:call-template>
         </xsl:for-each>
     </nodes>
</xsl:template>

<xsl:template name="findGroups">
     <xsl:param name="groups" />
     <print>
         <xsl:value-of select="$groups"></xsl:value-of>
     </print>
     <xsl:for-each select="/fix/components/component[@name=$groups]">
         <here>
             <xsl:call-template name="findGroups">
                 <xsl:with-param name="groups" select="group/component/@name"/>
             </xsl:call-template>
         </here>
    </xsl:for-each>
</xsl:template>

我已经看到使用密钥或其他技术解决类似问题的解决方案,但我的xslt / xpath还没有允许我调整这些,所以有人可以提供帮助吗?

感谢。

嗨,我还应该注意到会有多条消息。 目的是删除文档中消息标记中未引用的组(以及最终的其他字段)。 输出最终应该是

<fix>
    <messages>
        <message name="myMessage1">
            <field name="abc"/>
            <component name="component1"/>
            <component name="component2"/>
        </message>
        <message name="myMessage2">
            <field name="hello"/>
            <component name="component1"/>
        </message>
    </messages>
    <components>
        <component name="component1">
        </component>
        <component name="component2">
            <group name="agroup">
                <field name="afield"/>
                <component name="component3"/>
            </group>
        </component>
        <component name="component3"/>
        </component>
    </components>
</fix>

注意 - 已删除最终无法从消息节点访问的component4和其他标记。

好的,上面已经回答了,我现在遇到了另外一个问题。 实际上,xml文档的末尾有许多字段,沿着

<fix>
    <messages>
        <message/>
    <messages>
    groups>
        <group/>
    <groups>
    <fields>
        <field name="abc"/>
        <field name="bcd"/>
    <fields>
</fix>

我正在尝试仅保留名称与任何邮件或组位置中的字段匹配的字段,或者匹配gropu或组件名称。我能够保留最多,但不是全部,并且最终得到了令人讨厌的嵌套选择结构。有人可以提供更多指示吗?感谢。

e.g。我正在尝试添加以下内容: -

<xsl:key name="c3" match="message/field" use="@name" />
<xsl:key name="c4" match="group/field" use="@name" />
<xsl:key name="c5" match="group" use="@name" />
<xsl:key name="c6" match="component" use="@name" />

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

<xsl:template match="fields/field[not(key('c3', @name))]">
    <xsl:variable name="IsUsed">
        <xsl:apply-templates select="key('c4', @name)"/>
        <!-- <xsl:apply-templates select="key('c5', @name)"/>
        <xsl:apply-templates select="key('c6', @name)"/> -->
    </xsl:variable>
    <xsl:if test="$IsUsed != ''">
        <xsl:call-template name="identity" />
    </xsl:if>
</xsl:template>

<xsl:template match="group/field[not(key('c1', ../../@name))]" mode="IsUsed">
    <xsl:apply-templates select="key('c2', ../../@name)" mode="IsUsed"/>
</xsl:template>

<xsl:template match="group/field[key('c1', ../../@name)]" mode="IsUsed">
    <xsl:text>1</xsl:text>
</xsl:template>

我认为应该匹配使用消息的组中使用的字段。但是,这种情况并没有发生。

1 个答案:

答案 0 :(得分:1)

首先,我将从身份模板开始

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

就此而言,这会按原样复制所有节点。这意味着您采取的方法不是试图弄清楚您需要复制的内容,而是找出您不需要复制的内容。

为了帮助查找组件,我将定义两个键;一个用于查找原始“消息”组件,另一个用于查找“组”下出现的组件

<xsl:key name="c1" match="message/component" use="@name" />
<xsl:key name="c2" match="group/component" use="@name" />

现在,您只担心组件未被“消息”

直接引用的情况
<xsl:template match="components/component[not(key('c1', @name))]">

在此范围内,您将使用递归模板检查此“component”元素是否在其他地方被引用。

  <xsl:variable name="IsUsed">
     <xsl:apply-templates select="key('c2', @name)" mode="IsUsed"/>
  </xsl:variable>

虽然您可以为此“IsUsed”检查执行一个模板,但我将在两个中执行此操作:

<xsl:template match="group/component[key('c1', ../../@name)]" mode="IsUsed">
   <xsl:text>1</xsl:text>
</xsl:template>

<xsl:template match="group/component[not(key('c1', ../../@name))]" mode="IsUsed">
   <xsl:apply-templates select="key('c2', ../../@name)" mode="IsUsed"/>
</xsl:template>

因此,当父组件被“消息”使用时,第一个模板将匹配,如果是,则返回“1”,否则第二个模板将匹配,并进行递归调用以保持匹配。 / p>

试试这个XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="c1" match="message/component" use="@name" />
   <xsl:key name="c2" match="group/component" use="@name" />

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

   <xsl:template match="components/component[not(key('c1', @name))]">
      <xsl:variable name="IsUsed">
         <xsl:apply-templates select="key('c2', @name)" mode="IsUsed"/>
      </xsl:variable>
      <xsl:if test="$IsUsed != ''">
         <xsl:call-template name="identity" />
      </xsl:if>
   </xsl:template>

   <xsl:template match="group/component[not(key('c1', ../../@name))]" mode="IsUsed">
      <xsl:apply-templates select="key('c2', ../../@name)" mode="IsUsed"/>
   </xsl:template>

   <xsl:template match="group/component[key('c1', ../../@name)]" mode="IsUsed">
      <xsl:text>1</xsl:text>
   </xsl:template>
</xsl:stylesheet>