การรวมโหนดที่อยู่ติดกันประเภทเดียวกัน (XSLT 1.0)

เป็นไปได้หรือไม่ที่จะรวมทุกลำดับของโหนดที่เป็นประเภทเดียวกันที่ระบุ? ('aaa' ในกรณีนี้) (ไม่ใช่เพียงการปรากฏลำดับครั้งแรก)

นี่คืออินพุต XML ของฉัน:

<block>
    <aaa>text1</aaa>
    <aaa>text2</aaa>
    <aaa><xxx>text3</xxx></aaa>
    <bbb>text4</bbb>
    <aaa>text5</aaa>
    <bbb><yyy>text6</yyy></bbb>
    <bbb>text7</bbb>
    <aaa>text8</aaa>
    <aaa><zzz>text9</zzz></aaa>
    <aaa>texta</aaa>
</block>

และฉันต้องการผลลัพธ์ต่อไปนี้:

<block>
    <aaa>text1text2<xxx>text3</xxx></aaa>
    <bbb>text4</bbb>
    <aaa>text5</aaa>
    <bbb><yyy>text6</yyy></bbb>
    <bbb>text7</bbb>
    <aaa>text8<zzz>text9</zzz>texta</aaa>
</block>

ความช่วยเหลือใด ๆ ที่ชื่นชม


person Peter    schedule 26.11.2009    source แหล่งที่มา


คำตอบ (3)


นี่เป็นอีกวิธีหนึ่งในการทำเช่นนี้

ขั้นแรก ให้จับคู่โหนดย่อยทั้งหมดขององค์ประกอบบล็อก

<xsl:template match="block/child::*">

ถัดไป ตรวจสอบว่าพี่น้องที่ตรงที่สุดขององค์ประกอบมีชื่อที่แตกต่างกันหรือไม่ โดยระบุว่านี่คือองค์ประกอบแรกจากองค์ประกอบที่อยู่ติดกันตั้งแต่หนึ่งองค์ประกอบขึ้นไป:

<xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">

หากเป็นเช่นนั้น คุณสามารถคัดลอกโหนดนั้นได้ จากนั้นคุณจะต้องคัดลอกพี่น้องที่มีชื่อเดียวกันต่อไปนี้ ฉันทำสิ่งนี้โดยการเรียกเทมเพลตซ้ำ ๆ ในแต่ละพี่น้องที่มีชื่อเดียวกันทันที

<xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>

การรวมทั้งหมดนี้เข้าด้วยกันทำให้

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <!-- Match children of the block element -->
   <xsl:template match="block/child::*">
      <xsl:variable name="name" select="local-name()"/>

      <!-- Is this the first element in a sequence? -->
      <xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">
         <xsl:copy>
            <xsl:apply-templates />

            <!-- Match the next sibling if it has the same name -->
            <xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
         </xsl:copy>
      </xsl:if>
   </xsl:template>

   <!-- Recursive template used to match the next sibling if it has the same name -->
   <xsl:template match="block/child::*" mode="next">
      <xsl:variable name="name" select="local-name()"/>
         <xsl:apply-templates />
      <xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
   </xsl:template>

   <!-- Template used to copy a generic node -->
   <xsl:template match="@* | node()">
         <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
         </xsl:copy>
   </xsl:template>
</xsl:stylesheet>
person Tim C    schedule 27.11.2009
comment
สำหรับการแก้ปัญหาของฉัน ฉันเพียงแค่ต้องรวมโหนด 'aaa' เท่านั้น ดังนั้นฉันต้องปรับวิธีแก้ปัญหาของคุณเล็กน้อยโดยเปลี่ยน 'match=int:block/child::*' เป็น 'match=int:block/child::aaa' มิฉะนั้นก็จะทำงานได้อย่างไม่มีที่ติ - person Peter; 27.11.2009

สมมติว่าคุณมี block เพียงรายการเดียว วิธี Muenchian เป็นวิธีที่เหมาะสมที่สุด นี้:

<!-- group nodes by name -->
<xsl:key name="block-children-by-name" match="block/*" use="name()"/>

<!-- for nodes that aren't first in their group, no output -->
<xsl:template match="block/*" />

<!-- for nodes that are first in their group, combine group children and output -->
<xsl:template match="block/*[generate-id() =
                             generate-id(key('block-children-by-name', name())[1])]">
   <xsl:copy>
     <xsl:copy-of select="key('block-children-by-name', name())/*"/>
   </xsl:copy>
</xsl:template>

โปรดทราบว่าการดำเนินการนี้จะรวมเฉพาะโหนดย่อยเท่านั้น และไม่รวมเช่น คุณลักษณะใดๆ ที่อาจเกิดขึ้นบน aaa และ bbb เอง

person Pavel Minaev    schedule 26.11.2009

นี่เป็นอีกวิธีหนึ่งโดยไม่ต้องใช้เทมเพลตแบบเรียกซ้ำ

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

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

  <xsl:template match="aaa">
    <xsl:if test="not(preceding-sibling::*[1]/self::aaa)">
      <xsl:variable name="following" 
                    select="following-sibling::aaa[
                              not(preceding-sibling::*[
                                not(self::aaa) and
                                not(following-sibling::aaa = current())
                              ])
                            ]"/>
      <xsl:copy>
        <xsl:apply-templates select="$following/@*"/>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="node()"/>
        <xsl:apply-templates select="$following/node()"/>
      </xsl:copy>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

นิพจน์ XPath ที่ค่อนข้างซับซ้อนสำหรับการเลือกโหนดพี่น้อง aaa ต่อไปนี้ ซึ่งถูกรวมเข้ากับโหนดปัจจุบัน:

following-sibling::aaa[                       # following 'aaa' siblings
  not(preceding-sibling::*[                   #   if they are not preceded by
    not(self::aaa) and                        #     a non-'aaa' node
    not(following-sibling::aaa = current())   #     after the current node
  ])
]
person Jukka Matilainen    schedule 27.11.2009