如何在兄弟之间简化XML节点以确保使用XSLT不存在重复节点?

时间:2012-05-02 17:53:35

标签: xml xslt

如果我在xml中有这个输入文件:

<root> 
    <node id="N1">
        <fruit id="1">
            <orange id="x" action="create">
                <attribute>
                    <color>Orange</color>
                    <year>2000</year>
                </attribute>
            </orange>                        
        </fruit>        

        <fruit id="1">
            <orange id="x" action="create">
                <attribute>
                    <color>Orange</color>
                    <condition>good</condition>
                </attribute>
            </orange>                        
        </fruit>        
    </node>
</root>

这是预期的输出:

<root> 
    <node id="N1">
        <fruit id="1">
            <orange id="x" action="create">
                <attribute>
                    <color>Orange</color>
                    <year>2000</year>
                    <condition>good</condition>
                </attribute>
            </orange>                        
        </fruit>        

        <fruit id="1">                                 
        </fruit>
    </node>
</root>

如何在两个兄弟之间进行简化:

  1. 检查父项是否相同(fruit id = 1)
  2. 检查节点ID和操作是否相同(orange id = x action = create)
  3. 如果之前已经定义了子元素并且值相同(颜色为橙色),我们将其删除。
  4. 如果未定义第二个兄弟的子元素,我们将第二个节点添加到第一个节点。 (条件好)
  5. 如果先前已经定义了节点但是不同的值(比如颜色为红色),我们将节点原样保留。
  6. 另一种情况: 输入2:

    <root> 
        <node id="N1">
            <fruit id="1">
                <orange id="x" action="create">
                    <attribute>
                        <color>Orange</color>                   
                    </attribute>
                </orange>                        
            </fruit>        
    
            <fruit id="1">
                <orange id="x" action="create">
                    <attribute>
                        <color>Red</color>
                        <condition>good</condition>
                    </attribute>
                </orange>                        
            </fruit>        
        </node>
    </root>
    

    预期的输出:

    <root> 
        <node id="N1">
            <fruit id="1">
                <orange id="x" action="create">
                    <attribute>
                        <color>Orange</color>
                        <condition>good</condition>
                    </attribute>
                </orange>                        
            </fruit>        
    
            <fruit id="1">
                <orange id="x" action="create">
                    <attribute>
                        <color>Red</color>
                    </attribute>
                </orange>                        
            </fruit>        
        </node>
    </root>
    

    另一种情况:

    <root> 
        <nodeA id="A">
            <fruit id="1">
                <orange id="x" action="delete" />    <!-- no attributes here -->                                         
            </fruit>        
    
            <fruit id="1">
                <orange id="x" action="delete"/>   
                <orange id="y" action="delete" />                                            
            </fruit>        
        </nodeA>
    </root>
    

    预期产出:

    <root> 
        <nodeA id="A">
            <fruit id="1">
                <orange id="x" action="delete" />   
            </fruit>        
    
            <fruit id="1"> 
                <orange id="y" action="delete" />                                         
            </fruit>        
        </nodeA>
    </root>
    

    我希望这个例子给出明确的想法,请帮我转换文件。 感谢。

    约翰

1 个答案:

答案 0 :(得分:2)

John,这是一个有效的版本。它有些残酷和程序化,所以我想知道你是否真的想在XSLT中做这种逻辑。你走了:

以下样式表:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:key name="entity" match="node/*/*" use="concat(parent::*/@id, '_', @id, '_', @action)"/>

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

    <xsl:template match="node/*/*[not(attribute)][generate-id() != generate-id(key('entity', concat(parent::*/@id, '_', @id, '_', @action))[1])]"/>

    <xsl:template match="node/*/*[attribute]">
        <xsl:variable name="attributes">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()">
                    <xsl:with-param 
                            name="mode" 
                            select="generate-id() = generate-id(key('entity', concat(../@id, '_', @id, '_', @action))[1])"/>
                </xsl:apply-templates>
            </xsl:copy>
        </xsl:variable>
        <xsl:if test="$attributes/*/attribute/*">
            <xsl:copy-of select="$attributes"/>
        </xsl:if>
    </xsl:template>

    <xsl:template match="node/*/*/attribute">
        <xsl:param name="mode"/>
        <xsl:variable name="all-attributes" select="key('entity', concat(../../@id, '_', ../@id, '_', ../@action))/attribute/*"/>
        <xsl:copy>
            <xsl:if test="$mode = true()">
                <xsl:for-each-group select="$all-attributes" group-by="local-name()">
                    <xsl:copy>
                        <xsl:apply-templates select="@* | node()"/>
                    </xsl:copy>
                </xsl:for-each-group>
            </xsl:if>
            <xsl:if test="$mode = false()">
                <xsl:for-each select="*">
                    <xsl:variable 
                        name="same-name-attr" 
                        select="$all-attributes[local-name() = current()/local-name()][count(. | current()/preceding::*) = count(current()/preceding::*)]"/>
                    <xsl:if test="$same-name-attr and not(. = $same-name-attr)">
                        <xsl:copy-of select="."/>
                    </xsl:if>
                </xsl:for-each>
            </xsl:if>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

会产生以下结果

<root>
   <node id="N1">
      <fruit id="1">
         <orange id="x" action="create">
            <attribute>
               <color>Orange</color>
               <year>2000</year>
               <condition>good</condition>
               <new>!!</new>
            </attribute>
         </orange>
      </fruit>
      <fruit id="1">
         <orange id="x" action="create">
            <attribute>
               <color>Red</color>
            </attribute>
         </orange>
      </fruit>
      <fruit id="1">
         <orange id="x" action="create">
            <attribute>
               <color>Blue</color>
            </attribute>
         </orange>
      </fruit>
      <fruit id="1">
         <orange id="x" action="create">
            <attribute>
               <condition>ugly</condition>
            </attribute>
         </orange>
      </fruit>
      <fruit id="1"/>
   </node>
</root>

所有唯一属性都会被提取到第一次出现的create操作中,只有具有不同值属性的属性才会保留在following::个节点中。如果该节点没有任何新内容可以添加它,那么它就会被遗忘。以下是我如何确定该属性是否值得为后续事件保留。如果之前没有看到该属性,那么它已被拉到第一次出现,所以我们跳过它。如果之前已经看到它(=它在preceding轴上的同名属性集合中)并且具有不同的文本值,那么我们就保留它。

你想要做的选择器逐渐变得越来越复杂,所以我不得不使用临时变量基本上让模板对它进行了检查,然后检查是否有任何结果然后决定是否值得复制到结果树。可能有一种方法可以将此逻辑转换为匹配谓词,但我不确定它是否更具可读性。我希望这是有道理的。

更新我更新了解决方案以适用于您的无属性角落案例。我基本上不得不沉默重复的no - attribute节点,并使主模板更具体,只适用于attribute s的节点。将“重复”with-attribute节点的无属性节点将使用主要属性合并逻辑进行静默。需要保留的no - attribute节点将使用默认的标识转换进行复制。