XSLT en grouping

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • pkwarts
  • Registratie: April 2008
  • Laatst online: 23-05 22:19

pkwarts

555433800

Topicstarter
Goedendag,

Ik heb een probleem met wat XML en XSLT waarvan ik vrij zeker weet dat dit op te lossen valt dmv grouping. Ik weet echter niet hoe dit precies te implementeren.

De XML ziet er als volgt uit:

code:
1
2
3
4
<Normal paragraphNumber="1">Dit</Normal>
<Normal paragraphNumber="1"> is 1 zin</Normal>
<Normal paragraphNumber="2">En dit is de volgende</Normal>
<AndereTag>bla</AndereTag>

De hoeveelheid tags met dezelfde paragraphNumber staat niet vast. Het kan er maar eentje zijn, maar het kunnen er ook zes zijn. De structuur van de XML kan ik niks aan veranderen helaas.


Nu is het de bedoeling dat dmv de XSLT er iets uitkomt als het volgende:
code:
1
2
<p><span>Dit</span><span> is 1 zin</span></p>
<p><span>En dit is de volgende</span></p>


De tags hebben nog meer attributen waarmee CSS classes aan de spans worden meegegeven, vandaar de spans. Optioneel mag dit ook de uitkomst worden:

code:
1
2
<p><span>Dit</span><span> is 1 zin</span></p>
<p>En dit is de volgende</p>


Ik heb uiteraard onderzoek gedaan voor dit topic te openen en ik kwam uiteindelijk de Muenchian method tegen. Ik heb geprobeerd dit te implementeren voor mijn situatie, maar tot nu toe geen succes. Als iemand mij op weg zou kunnen helpen zou ik deze persoon eeuwig dankbaar zijn ;) Laat het me ook weten als ik iets niet duidelijk heb neergezet.

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
pkwarts schreef op zondag 06 februari 2011 @ 09:46:
Ik heb uiteraard onderzoek gedaan voor dit topic te openen en ik kwam uiteindelijk de Muenchian method tegen. Ik heb geprobeerd dit te implementeren voor mijn situatie, maar tot nu toe geen succes. Als iemand mij op weg zou kunnen helpen zou ik deze persoon eeuwig dankbaar zijn ;) Laat het me ook weten als ik iets niet duidelijk heb neergezet.
Je zou eens kunnen beginnen met posten wat je dan precies hebt; misschien dat we daar iets aan zien.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • pkwarts
  • Registratie: April 2008
  • Laatst online: 23-05 22:19

pkwarts

555433800

Topicstarter
Ik ben begonnen met het maken van een key:

code:
1
<xsl:key name="normalspan" match="Normal" use="@paragraphNumber" />


Vervolgens code waar ik vrij zeker van ben dat deze niet klopt:
code:
1
2
3
4
5
6
7
8
9
10
11
    <xsl:template match="normalspan">
        <p>
        <xsl:apply-templates
          select="*[Normal and
                    generate-id(.)=generate-id(key('normalspan', @paragraphNumber))]" />
        </p>
    </xsl:template>

    <xsl:template match="*[@paragraphNumber]">
        <span><xsl:value-of select="." /></span>
    </xsl:template>


De onderste doet het wel, met firebug zie ik keurig <span> verschijnen rondom de verschillende stukken tekst. Ik krijg ze alleen niet gegroepeerd in <p> tags.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:34
In XQuery 1.0 zou ik het ongeveer zo oplossen:

XQuery:
1
2
3
4
5
6
for n in distinct-values(./Normal/@paragraphNumber)
order by $n
return <p>{
  for t in ./Normal/[@paragraphNumber=$n]/text()
  return <span>{$t}</span>
}</p>

Dit moet ook naar XSLT te porten zijn, lijkt me.

XQuery 1.1 (en XSLT 2.0 (?)) hebben support voor group-by waarbij je misschien wat efficiënters kunt programmeren, maar ik denk dat je nog steeds niet echt van de lus-in-een-lus afkomt.

offtopic:
Ik verbaas me altijd over de populariteit van XSLT, want ik vind XQuery zó veel intuïtiever om mee te werken, en je kunt er ongeveer hetzelfde mee.

[ Voor 17% gewijzigd door Soultaker op 06-02-2011 15:34 ]


Acties:
  • 0 Henk 'm!

  • pkwarts
  • Registratie: April 2008
  • Laatst online: 23-05 22:19

pkwarts

555433800

Topicstarter
Komen op jou manier niet alle Normal tags onder elkaar te staan? Dat is namelijk in de XML niet het geval. Hier staan ook andere tags tussen en het moet allemaal in die volgorde afgehandeld worden. Voorbeeldje:
code:
1
2
3
4
5
<Normal paragraphNumber="1">Dit</Normal>
<Normal paragraphNumber="1"> is 1 zin</Normal>
<Normal paragraphNumber="2">En dit is de volgende</Normal>
<Heading1>Dit is een header</Heading1>
<Normal paragraphNumber="3">En hier gaan we weer verder  met normal</Normal>


In de html moet dit uiteindelijk dit worden:
code:
1
2
3
4
<p><span>Dit</span><span> is 1 zin</span></p>
<p><span>En dit is de volgende</span></p>
<h1>Dit is een header</h1>
<p><span>En hier gaan we weer verder met normal</span></p>


Ik geef toe, het is XML op z'n slechtst, maar ik krijg het op deze manier aangeleverd.

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:34
Je hebt gelijk; daar had ik geen rekening mee gehouden... hmm, je zou dan eigenlijk opvolgende <Normal's> met hetzelfde paragraphNumber eerst naar een paragraaf moeten mappen. Ik weet er niet 1-2-3 een handige aanpak voor. Ik zou eerlijk gezegd een imperatieve aanpak overwegen als de documenten zo weinig structuur hebben.

Acties:
  • 0 Henk 'm!

Anoniem: 84120

Het volgende werkt in Internet Explorer 7 (de beste en enigste XML/XSLT ding die ik nu tot mijn beschikking heb):

XML:
1
2
3
4
5
6
7
8
9
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="style.xslt"?>
<root>
<Normal paragraphNumber="1">Dit</Normal>
<Normal paragraphNumber="1"> is 1 zin</Normal>
<Normal paragraphNumber="2">En dit is de volgende</Normal>
<Heading1>Dit is een header</Heading1>
<Normal paragraphNumber="3">En hier gaan we weer verder  met normal</Normal>
</root>


XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0"?>

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

    <xsl:key name="pn" match="Normal" use="@paragraphNumber" />

    <xsl:template match="/root">
        <html>
            <xsl:for-each select="Normal[generate-id() = generate-id(key('pn', @paragraphNumber)[1])]">
                <p>
                    <xsl:for-each select="key('pn',@paragraphNumber)">
                        <xsl:value-of select="text()" />
                    </xsl:for-each>
                </p>
            </xsl:for-each>
        </html>
    </xsl:template>
</xsl:stylesheet>


geeft
code:
1
2
3
4
5
<html>
<p>Dit is 1 zin</p>
<p>En dit is de volgende</p>
<p>En hier gaan we weer verder met normal</p>
</html>

terug.

Is dat ongeveer wat je wilt (zelf sorteren toevoegen als nodig en fixxen voor de bugs die ik over het hoofd zie doordat ik helemaal niets fatsoenlijk kan testen)? Wat het doet: Maak een sleutel (pn) over Normal tags. Gebruik als sleutel het paragraphNumber attribute van deze Normal tags (regel 6). Regel 10: voor alle Normal tags z1 doe:
[list]
• pak alle Normal tags met hetzelfde paragraphNumber (dit doet key).
• Kies de eerste van al deze Normal tags; laat dit tag z2 zijn.
• Hang aan deze eerste tag een uniek id (voor deze node).
• Hang ook aan je z1 een uniek id.
• ALS uniek id van z1 en z2 gelijk zijn: kies deze node.

Effectief: selecteer elk paragraaf nummer eenmaal. Vervolgens (regel 12) voor alle nodes met hetzelfde paragraaf nummer: print de inhoud van dit ding.

Noot: dit kan natuurlijk veel mooier in XSLT<nieuwere versies>. Maar ik weet niet welke versie jij wilt; en ik kan nu alleen in Internet Explorer testen!

[ Voor 41% gewijzigd door Anoniem: 84120 op 07-02-2011 12:24 ]


Acties:
  • 0 Henk 'm!

  • pkwarts
  • Registratie: April 2008
  • Laatst online: 23-05 22:19

pkwarts

555433800

Topicstarter
Ik zal dit vanmiddag allemaal even testen, maar het ziet er nu al goed uit! Verwacht vanmiddag nog een bericht.

Acties:
  • 0 Henk 'm!

  • pkwarts
  • Registratie: April 2008
  • Laatst online: 23-05 22:19

pkwarts

555433800

Topicstarter
Ik heb even naar de XSLT gekeken en ik snap wat je precies doet. Echter zal dit als output ook alle Normal tags onder elkaar weergeven. De XML heeft geen goede structuur en ik moet alles in volgorde afhandelen. Je ziet bijvoorbeeld ook in de voorbeeld XML die je hebt gebruikt dat er bijvoorbeeld een Heading tussenstaat. In de XSLT kan je die niet op de goede plek verwerken.

Steek er niet meer teveel moeite in, want ik heb te horen gekregen dat de XML blijkbaar ook op de volgende manier aangeleverd kan worden:
code:
1
2
3
4
<Paragraph>
<Normal>Dit</Normal>
<Normal is een zin</Normal
</Paragraph>


Aangezien hier makkelijk over te itereren valt is mijn probleem opgelost. Mocht je het antwoord uit je hoofd weten dan zou ik het alsnog wel graag willen weten (ben altijd bereid wat meer XSLT te leren), maar steek er geen moeite meer in.

Wel wil ik je alsnog heel erg bedanken voor de uitleg, ik heb er zeker wat van opgestoken.

Acties:
  • 0 Henk 'm!

Anoniem: 84120

Voor elk bestand dat je kan aanleveren kan ik wel een oplossing vinden die jou uitvoer geeft (XSLT is HEEL flexibel; tis een complete programmeertaal). Maar als je iets ingewikkelder wilt (alleen Normals bij elkaar die `bij elkaar staan'; Headings er tussen en dergelijke): dan wordt de oplossing (XSLT1) al heel snel heel traag, inefficiënt en compleet onleesbaar.

Dus: gebruik het betere formaat maar zou ik aanraden. Maar aangezien ik je Headings over het hoofd heb gezien en die voor je voorbeeld invoer erg simpel zijn krijg je die cadeau.

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0"?>

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

    <xsl:key name="pn" match="Normal" use="@paragraphNumber" />

    <xsl:template match="/root">
        <html>
            <xsl:for-each select="Normal[generate-id() = generate-id(key('pn', @paragraphNumber)[1])]">
                <xsl:variable name="currentNormal" select="." />
                <xsl:for-each select="//*[substring(name(),1,7) = 'Heading'][(./following-sibling::*)[1] = $currentNormal]">
                    <xsl:variable name="header">h<xsl:value-of select="substring(name(),8,1)" /></xsl:variable>
                    <xsl:element name="{$header}">
                        <xsl:value-of select="text()" />
                    </xsl:element>
                </xsl:for-each>
                <p>
                    <xsl:for-each select="key('pn',@paragraphNumber)">
                        <span><xsl:value-of select="text()" /></span>
                    </xsl:for-each>
                </p>
            </xsl:for-each>
        </html>
    </xsl:template>
</xsl:stylesheet>


Noot: je kan zelf wel nalopen waarom deze nooit supersnel gaat worden. Regel 12 selecteert elke Heading* direct voor de huidige/eerste Normal tag met een bepaalde paragraphNumber. Vervolgens wordt van deze Heading* een hn-tag gemaakt met n het eerste karakter direct na Heading. Dit is zeker niet een schoonheidsprijs waard.

[ Voor 8% gewijzigd door Anoniem: 84120 op 07-02-2011 15:41 ]

Pagina: 1