XSLT 1.0 เพื่อวนซ้ำองค์ประกอบ XML หลายรายการด้วยค่าที่คั่นด้วยเครื่องหมายจุลภาค

ฉันมีเอกสาร XML ที่มีโครงสร้างดังนี้

<items>
 <item>
  <name>item1</name>
  <attributes>a,b,c,d</attributes>
 </item>
 <item>
  <name>item2</name>
  <attributes>c,d,e</attributes>
 </item>
</items>

สำหรับค่าแอตทริบิวต์ที่ไม่ซ้ำกันแต่ละค่า (คั่นด้วยเครื่องหมายจุลภาค) ฉันจำเป็นต้องแสดงรายการชื่อรายการทั้งหมดที่เกี่ยวข้องกับค่านั้นดังนี้:

a : item1
b : item1
c : item1, item2
d : item1, item2
e : item2

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

Attribute[not(.=following::Attribute)]

แต่เนื่องจากผลลัพธ์ของเทมเพลตไม่ใช่ชุดโหนดที่เคยผ่านตัวแยกวิเคราะห์ XML ฉันจึงไม่สามารถสำรวจได้ ฉันยังลองใช้ฟังก์ชัน node-set() ของ exslt เพียงเพื่อจะรู้ว่ามันไม่อนุญาตให้ฉันสำรวจแต่ละโหนด Attribute เช่นกัน

ณ จุดนี้ ฉันไม่รู้ว่ามีวิธีง่ายๆ ในการทำเช่นนี้ และขอขอบคุณสำหรับความช่วยเหลือหรือแนวคิดในการดำเนินการต่อไป ขอบคุณ!


person ratherOCD    schedule 06.04.2011    source แหล่งที่มา
comment
คำถามที่ดี +1 ดูคำตอบของฉันสำหรับวิธีแก้ปัญหาและคำอธิบายที่สมบูรณ์   -  person Dimitre Novatchev    schedule 06.04.2011
comment
ฉันสนุกกับการคิดถึงคำถามนี้   -  person Wayne    schedule 06.04.2011


คำตอบ (2)


การเปลี่ยนแปลงนี้:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 xmlns:ext="http://exslt.org/common">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kAtrByVal" match="attr" use="."/>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <groups>
    <xsl:apply-templates/>
   </groups>
  </xsl:variable>

  <xsl:variable name="vPass1"
       select="ext:node-set($vrtfPass1)"/>

  <xsl:apply-templates select="$vPass1/*"/>
 </xsl:template>

 <xsl:template match="item">
  <group name="{name}">
   <xsl:apply-templates select="attributes"/>
  </group>
 </xsl:template>

 <xsl:template match="attributes" name="tokenize">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText)">
   <xsl:variable name="vText" select=
        "concat($pText,',')"/>
   <attr>
    <xsl:value-of select="substring-before($vText,',')"/>
   </attr>
   <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select=
    "substring-after($pText,',')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>

 <xsl:template match=
  "attr[generate-id()
       =
        generate-id(key('kAtrByVal',.)[1])
       ]
  ">
  <xsl:value-of select="concat('&#xA;',.,': ')"/>

  <xsl:for-each select="key('kAtrByVal',.)">
   <xsl:value-of select="../@name"/>
   <xsl:if test="not(position()=last())">
    <xsl:text>, </xsl:text>
   </xsl:if>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

เมื่อนำไปใช้กับเอกสาร XML ที่ให้มา:

<items>
    <item>
        <name>item1</name>
        <attributes>a,b,c,d</attributes>
    </item>
    <item>
        <name>item2</name>
        <attributes>c,d,e</attributes>
    </item>
</items>

สร้างผลลัพธ์ที่ต้องการและถูกต้อง:

a: item1
b: item1
c: item1, item2
d: item1, item2
e: item2

คำอธิบาย:

  1. Pass1: โทเค็นไนซ์และผลลัพธ์สุดท้าย:

<groups>
  <group name="item1">
    <attr>a</attr>
    <attr>b</attr>
    <attr>c</attr>
    <attr>d</attr>
  </group>
  <group name="item2">
    <attr>c</attr>
    <attr>d</attr>
    <attr>e</attr>
  </group>
</groups>

.2. Pass2 รับผลลัพธ์ของ Pass1 (แปลงเป็นชุดโหนดโดยใช้ฟังก์ชันส่วนขยาย ext:node-set()) เป็นอินพุต ดำเนินการจัดกลุ่ม Muenchian และสร้างผลลัพธ์สุดท้ายที่ต้องการ

person Dimitre Novatchev    schedule 06.04.2011
comment
ยังคงมีปัญหาเล็กน้อยกับเรื่องนี้ <xsl:template match="text()"/> ใช้เพื่อให้เมื่อ <xsl:apply-templates select="$vPass1/*"/> ถูกเรียกว่าเทมเพลตการจัดกลุ่ม Muenchian สามารถทำสิ่งนั้นได้หรือไม่ - person ratherOCD; 06.04.2011
comment
@ratherOCD: ไม่ เทมเพลตนี้จะแทนที่เทมเพลตในตัว XSLT สำหรับโหนดข้อความ -- และทำให้แน่ใจว่าโหนดข้อความจะไม่ถูกส่งออกเนื่องจากผลลัพธ์ของ <xsl:apply-templates/> - person Dimitre Novatchev; 06.04.2011
comment
ขอบคุณ ฉันทำทุกอย่างได้สำเร็จ มีวิธีที่ดีในการจัดเรียง attr ตามลำดับตัวอักษรหรือไม่? - person ratherOCD; 06.04.2011
comment
@ratherOCD: ใช่ แทนที่ xsl:apply-templates select="$vPass1/*/> ด้วย <xsl:apply-templates select="$vPass1/*/*/attr"> <xsl:sort/> </xsl:apply-templates> - person Dimitre Novatchev; 06.04.2011

ความคิดแรกของฉันคือการจ่ายบอลสองครั้ง ขั้นแรกให้โทเค็นองค์ประกอบ attributes โดยใช้เวอร์ชันที่แก้ไข (เล็กน้อย) ของ @คำตอบของ Alejandro คำถามก่อนหน้านี้:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <items>
            <xsl:apply-templates/>
        </items>
    </xsl:template>
    <xsl:template match="item">
        <item name="{name}">
            <xsl:apply-templates select="attributes"/>
        </item>
    </xsl:template>
    <xsl:template match="attributes" name="tokenize">
        <xsl:param name="text" select="."/>
        <xsl:param name="separator" select="','"/>
        <xsl:choose>
            <xsl:when test="not(contains($text, $separator))">
                <val>
                    <xsl:value-of select="normalize-space($text)"/>
                </val>
            </xsl:when>
            <xsl:otherwise>
                <val>
                    <xsl:value-of select="normalize-space(
                        substring-before($text, $separator))"/>
                </val>
                <xsl:call-template name="tokenize">
                    <xsl:with-param name="text" select="substring-after(
                                    $text, $separator)"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

Which produces:

<items>
    <item name="item1">
        <val>a</val>
        <val>b</val>
        <val>c</val>
        <val>d</val>
    </item>
    <item name="item2">
        <val>c</val>
        <val>d</val>
        <val>e</val>
    </item>
</items>

จากนั้นใช้สไตล์ชีตต่อไปนี้กับเอาต์พุตนั้น:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="byVal" match="val" use="." />
    <xsl:template match="val[generate-id() = 
                             generate-id(key('byVal', .)[1])]">
        <xsl:value-of select="." />
        <xsl:text> : </xsl:text>
        <xsl:apply-templates select="key('byVal', .)" mode="group" />
        <xsl:text>&#10;</xsl:text>
    </xsl:template>
    <xsl:template match="val" mode="group">
        <xsl:value-of select="../@name" />
        <xsl:if test="position() != last()">
            <xsl:text>, </xsl:text>
        </xsl:if> 
    </xsl:template>
    <xsl:template match="val" />
</xsl:stylesheet>

การผลิต:

a : item1
b : item1
c : item1, item2
d : item1, item2
e : item2

การทำเช่นนี้ในสไตล์ชีตเดียวจะต้องใช้ความคิดมากกว่านี้ (หรือฟังก์ชันส่วนขยาย)

person Wayne    schedule 06.04.2011