0

我有这个输入文件:

<root> 
    <library id="L1">
        <shelf1 id="1">
            <book id="1" action="borrow">
                <attributes>
                    <user>John</user>                    
                </attributes>
                <other1>y</other1>
            </book>  
            <book id="1" action="extend">
                <attributes>
                    <user>Woo</user>           
                    <length>3</length>
                </attributes>
                <other2>y</other2>
            </book>  
            <book id="1" action="extend">
                <attributes>
                    <length>2</length>
                    <condition>ok</condition>
                </attributes>
                <other3>y</other3>
            </book>
            <book id="2" action="borrow">...</book>
        </shelf1>
        <shelf2>...</shelf2>
    </library>
</root>

预期输出:

<root> 
    <library id="L1">
        <shelf1 id="1">
            <book id="1" action="borrow">
                <attributes>
                    <user>Woo</user>           
                    <length>2</length>
                    <condition>ok</condition>
                </attributes>
                <other1>y</other1>
                <other2>y</other2>
                <other3>y</other3>
            </book>
            <book id="2" action="borrow">...</book>
        </shelf1>
        <shelf2>...</shelf2>
    </library>
</root>

我的 XSL:

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

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

    <xsl:template match="library/*/*[1]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <attributes>
                <xsl:for-each-group select="attributes/*" group-by="name()">
                    <xsl:sort select="current-grouping-key()"/>
                    <xsl:apply-templates select="."/>
                </xsl:for-each-group>
            </attributes>
            <xsl:apply-templates select="*[not(self::attributes)]"/>
        </xsl:copy>            
    </xsl:template>

    <xsl:template match=
      "library/*/*
        [@action='extend' and following-sibling::*[1][@action='extend']
       or preceding-sibling::*[@action='borrow']]"/>
</xsl:stylesheet>

对于每个节点action=borrow后跟一个或多个节点action=extend

  • 将其合并到带有 的节点action=borrow
  • 将孩子合并attributes在一起,使其具有兄弟姐妹的所有独特属性和最新值。
  • 保持其他孩子不变

请让我知道如何使用 XSLT 2.0 修复此转换?

非常感谢。

亲切的问候,约翰

4

1 回答 1

3

You've asked a number of similar questions. Maybe it's time to buy a book and spend a couple of days reading? This is probably the last time I am going to answer a similar question from you. You should be able to learn from answers so that you never have to ask similar questions. If you are not learning, you have to ask yourself: "What is holding you back?"

Any way, lets have a look at this style-sheet. In the explanation, I will refer to points as numbers within xml or xpath comments. For example point 1 is demarcated <!-- 1 --> or if inside an xpath expression, then (: 1 :) . Obviously, remove the comments for production.

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:John="http://stackoverflow.com/questions/11463900"
  exclude-result-prefixes="xsl xs fn John">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*" />

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

 <xsl:template match="*[starts-with(local-name(),'shelf')]   (: 1 :)">
  <xsl:copy>
   <xsl:apply-templates select="@*" />

    <!-- 2 -->
   <xsl:apply-templates select="
     book[@action='extend']                                  (: 3 :)
         [not( preceding-sibling::book[@action='borrow'])]   (: 4 :)" />

    <!-- 5 -->
   <xsl:for-each-group
    select="book[@action='borrow']     (: 6 :)
             |                         (: 7 :)
            book[@action='extend']
                [preceding-sibling::book[@action='borrow']]   (: 8 :)"
    group-starting-with="book[@action='borrow']">
     <xsl:for-each select="current-group()[1]">
      <xsl:copy>                                            <!-- 9 -->
       <xsl:apply-templates select="@*" />
        <xsl:call-template name="merge-books-deeply">       <!-- 10 -->
         <xsl:with-param name="books" select="current-group()" />
         <xsl:with-param name="name-path" select="()" />
        </xsl:call-template>
      </xsl:copy>
     </xsl:for-each>     
   </xsl:for-each-group>

   <xsl:apply-templates select="                              (: 11 :)
     node()[ not( self::book[@action=('borrow','extend')])]" />

  </xsl:copy>
 </xsl:template>

<xsl:function name="John:children-on-path" as="element()*">
 <xsl:param name="base" as="element()*" />     <!-- 12 -->
 <xsl:param name="path" as="xs:string*" />     <!-- 13 -->
 <xsl:choose>
  <xsl:when test="fn:empty($base)">
   <xsl:sequence select="()" />
  </xsl:when>
  <xsl:when test="fn:empty($path)">
   <xsl:copy-of select="$base/*" />           <!-- 14 -->
  </xsl:when>
  <xsl:otherwise>
   <xsl:sequence select="John:children-on-path(
     $base/*[name()=$path[1]],                      (: 15 :)
     $path[position() ne 1])" /> 
  </xsl:otherwise>  
 </xsl:choose>
</xsl:function>

<xsl:template name="merge-books-deeply">
 <xsl:param name="books" as="element()*" />
 <xsl:param name="name-path" as="xs:string*" />
  <xsl:for-each-group
      select="John:children-on-path($books,$name-path)"
      group-by="name()">                                <!-- 16 -->                 
   <xsl:for-each select="current-group()[last()]" >     <!-- 17 -->
    <xsl:copy>
     <xsl:apply-templates select="@*" />
        <xsl:call-template name="merge-books-deeply">   <!-- 18 -->
         <xsl:with-param name="books" select="$books" />
         <xsl:with-param name="name-path" select="$name-path,name()" />
        </xsl:call-template>
     <xsl:apply-templates select="text()" />        
    </xsl:copy>
   </xsl:for-each>
  </xsl:for-each-group>
</xsl:template>

</xsl:stylesheet>

Explanation

  1. We start with the template for shelf nodes. It's unfortunate that you didn't just make the shelf node name constant. It would have made the match expression much simpler then. We copy the shelf node and any attributes.
  2. Now books are going to be merged according to some rules. So the books that get merged together are a group. By your stated merge rules, a group of books are all the books that start with a borrowed book and include following extended books. The natural candidate to achieve this will be the xsl:for-each-group instruction on borrowed and extended books, with @group-starting-with set to the head borrowed book (points 5 through to 8). There is a slight problem with xsl:for-each-group in that if you have any orphaned books, that is to say extended books not preceded by a borrowed book, then these will be the first group. We don't want this because it doesn't fit the merging rules. So first we just exclude them from grouping (see point 8) and process as normal any orphaned books at point 2.
  3. The predicate here selects the extended books.
  4. ... and this one selects the orphaned books
  5. Now we group the books of the shelf into mergeable groups
  6. Definitely if a book is borrowed it is in a group (In fact it will be the head of the group)
  7. and also we need to include the extended books that follow
  8. but exlcude the orphans
  9. current-group() at this point selects a group of mergeable books. The head of this sequence, current-group()[1] is the borrowed book that starts the group. We select it and copy it and its attributes.
  10. Now for the merging fun! It appears from your sample data that your are merging at two levels, to wit, nodes named like 'other1' and children of the node named 'attributes'. While your intentions are not clear as to how far this goes, I have taken the most liberal interpretation, which is to say that you want all child-of-book elements merged recursively to infinite depth. To do this we are going to need and call recursively a named template. The job of merge-books-deeply is to merge a set of books ($books) along some element path ($name-path). $name-path will start out as empty. It will then have values like ('attributes'),('attributes','user') and ('other1').
  11. At this point, we are still in the shelf, but we have completed our book merging. There may be non-book children or other strange things that didn't take part in the merging. Since they might have value, we copy them here.
  12. Now we need a support function to find all the merge-able nodes of our merge group at a given relative path from the shelf. This is John:children-on-path(). It takes two parameters. The $base is the nodes to be merged and
  13. $path is the path to navigate down from them. The function returns the sequence of nodes which are the children of $base descended by $path. For example if $base was /root and $path was ('library'), then John:children-on-path() should return shelf1 and shelf2. The function is defined recursively. First we take care of the trivial cases, then if not trivial we navigate one step down in the path, and eliminate the step that we have just taken from the next path and call continue until we have traversed the entire path.
  14. Once we find the merge-able nodes in the group, we do a new grouping. We group them by name. For example, all the user nodes is one group and then all the length nodes is another.
  15. Once we have got a group, say all users, there we cant copy them all - that the whole point of merging. So we pick one to prevail as the value for the whole group. In the sample data, the OP has chosen the last value. So that's what we do. We select that element, copy it and its attributes.
  16. And now we may have to merge again at another level. So we recurse back into the same template but with out path extended to include the new step that we have just taken.

Research for John

  1. Google for xsl:for-each-group and do some reaing. Here is a couple of good ones.
  2. Buy an XSLT book and read it. There are plenty to choose from.
于 2012-07-13T09:11:21.450 回答