0

如果我在 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. 检查父母是否相同(水果id = 1)
  2. 检查节点id和action是否相同(橙色id=x action=create)
  3. 如果之前已经定义了子元素并且值相同 (color-orange) ,我们将其删除。
  4. 如果之前没有定义第二个兄弟的子元素,我们将第二个节点添加到第一个节点。(条件良好)
  5. 如果之前已经定义了节点但值不同(比如红色),我们将节点保持原样。

另一种情况:输入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>

我希望该示例给出清晰的想法,并请帮助我处理转换文件。谢谢。

约翰

4

1 回答 1

2

约翰,这是一个有效的版本。这有点残酷和程序化,所以我想知道您是否真的想在 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)并且具有不同的文本值,那么我们才保留它。

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

更新我更新了解决方案,也适用于您的无属性极端情况。我基本上不得不使attribute重复的 no- 节点保持沉默,并使主模板更加具体,以便仅在带有attributes 的节点上工作。将使用主要属性合并逻辑“重复”具有属性节点的无属性节点将保持沉默。需要保留的 no- attributes 节点将使用默认的身份转换进行复制。

于 2012-05-03T20:13:20.687 回答