Toon posts:

[XSL] Probleem met verwerken van XML-data

Pagina: 1
Acties:

Verwijderd

Topicstarter
Sorry voor de wat onduidelijke topictitel, maar ik kan het volgende probleem niet (goed) in één zinnetje omschrijven. Het gaat hierom.

In de meest simpele situatie, zou een stukje XML er zo uit kunnen zien:

code:
1
2
3
4
5
6
7
8
9
10
11
<story> 
  <title>Linuxcare Responds To Tim O'Reilly's Article</title> 
  <url>http://slashdot.org/article.pl?sid=00/05/15/0254252</url> 
  <time>2000-05-15 02:57:07</time> 
  <author>timothy</author> 
  <department>consider-source-horses-mouth-grain-of-salt</department> 
  <topic>linuxbiz</topic> 
  <comments>142</comments> 
  <section>articles</section> 
  <image>topiclinuxbiz.gif</image> 
 </story>


In zo'n situatie zou het me wel lukken om met behulp van XSL bijvoorbeeld een MySQL-query te maken, waardoor de XML-data dus eigenlijk terecht komt in een database, die later dan weer gebruikt kan worden om data mee te serveren op andere pagina's. Dat gaat bijvoorbeeld zo:

code:
1
2
3
4
5
6
<xsl:template match="backslash/story">insert ignore into stories
(story_title, story_url, story_date)
values ("<xsl:value-of select="title"/>",
"<xsl:value-of select="url"/>",
"<xsl:value-of select="time"/>"); 
</xsl:template>


Ik zit echter met de volgende, iets minder prettig gegenereerde XML-code:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<value><struct>
<member><name>city_id</name>
<value><int>-2140176</int></value>
</member>
<member><name>countrycode</name>
<value><string>nl</string></value>
</member>
<member><name>name</name>
<value><string>Foo</string></value>
</member>
<member><name>count</name>
<value><int>8</int></value>
</member>
<member><name>languagecode</name>
<value><string>en</string></value>
</member>
</struct></value>


Met mijn relatief beperkte XSL-kennis, kan ik met geen mogelijkheid deze data in iets bruikbaars omzetten, zoals in het voorbeeld met slashdot helemaal bovenaan. Er zal waarschijnlijk nog e.e.a. aan syntax bijmoeten. Het probleem zit 'm eigenlijk in de manier waarop de XML-data is opgemaakt. Het is in dit geval dus <member><name>Name</name><value>Foo</value></member> in plaats van <name>Foo</name>.

Hoe kan ik deze XML-data verwerken? Wie kan me verder helpen? :Y)

  • 0528973
  • Registratie: Juni 2003
  • Laatst online: 15-05-2013
Hi,

ik weet niet precies wat je wilt maar ik denk dat ik met mijn waarschijnlijk
nog beperktere kennis een behulpzaam voorbeeldje heb gemaakt voor je.

zie http://www.w3schools.com/xsl/ voor betere uitleg.

example.xsl bevat :
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<html>
   <head><title>XML - XLST testje</title></head>
   <body>
   <table>
   <xsl:for-each select="value/struct/member">
      <tr>
         <td><xsl:value-of select="name" /></td>
         <xsl:for-each select="value">
         <td><xsl:value-of select="string" />
                 <xsl:value-of select="int" /></td>
         </xsl:for-each>
      </tr>
   </xsl:for-each>
   </table>
   </body>
</html>
</xsl:template>

</xsl:stylesheet>


example.xml bevat:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="example.xsl"?>
<value><struct>
<member><name>city_id</name>
<value><int>-2140176</int></value>
</member>
<member><name>countrycode</name>
<value><string>nl</string></value>
</member>
<member><name>name</name>
<value><string>Foo</string></value>
</member>
<member><name>count</name>
<value><int>8</int></value>
</member>
<member><name>languagecode</name>
<value><string>en</string></value>
</member>
</struct></value>


het levert op qua uitvoer
code:
1
2
3
4
5
city_id -2140176 
countrycode nl 
name Foo 
count 8 
languagecode en

[ Voor 89% gewijzigd door 0528973 op 28-06-2004 14:27 ]

Pascal


Verwijderd

de xslt van 0528973 is al redelijk op de goede weg. zou alleen geen foreach gebruiken, maar een gewone apply-templates. verder kan je d8 ik ipv de string en de int element op te zoeken, ook [1] gebruiken om het eerste element in die node te vinden

edit: of wat misschien ook wel ok is
Om ervopor te zorgen dat er soms wel en soms geen quotes om je waarde staan een choose te gebruiken en kijken wat voor waarde er is. Ik ben ervan uitgegaan dat er niet een string en een int element als value staat.

code:
1
2
3
4
<xsl:choose>
 <xsl:when test="int"><xsl:value-of select="int" /></xsl:when>
 <xsl:otherwise>'<xsl:value-of select="string" />'</xsl:otherwise>
</xsl:choose>

[ Voor 58% gewijzigd door Verwijderd op 28-06-2004 14:54 . Reden: code tags ]


Verwijderd

Gewoon value/struct de root maken, for-each 'member', en dan value-of name en value-of value.

Wellicht kun je van logica gebruikmaken (zoals when) maar je maakt het jezelf moeilijk door onzinnig te nesten. Een voorstel met beter verwerkbare XML-code:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--
Erg twijfelachtig waarom je value/struct gebruikt, maar goed ik laat het er ff in staan
voor de duidelijkheid...
-->
<value><struct>
<members>
<member id="1">
<properties>
<property id="0" name="city_id"/>
<value type="int">-2140176</value>
</property>
</member>
</members>
</struct></value>


I.v.m. dat brakke input-venstertje van het forum hier kan ik niet inspringen (heb geen grote moeite gedaan, voor alle duidelijkheid O-)) maar het idee lijkt me wel aardig.

De waardes van attributes van tags kun je (als ik het goed) heb dmv het @-teken voor de naam van het attribuut opvragen. Bestudeer de XPath-syntax voor meer info hieromtrent.

Als je XSLT eenmaal onder de knie hebt is erg leuk en kun je er bijzonder veel mee, het is minder moeilijk dan het lijkt. Die XSL tutorials op w3schools.org zijn idd aan aanradertje. :)

[ Voor 10% gewijzigd door Verwijderd op 29-06-2004 00:12 ]


  • Genoil
  • Registratie: Maart 2000
  • Laatst online: 12-11-2023
ik verveel me dood. en zoals iruoy al zegt, xsl:for-each is in de meeste gevallen nergens goed voor (en ik probeer choose constructies ook zo veel mogelijk te voorkomen ;):

XSLT:
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
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:output method="text" 
                indent="no"
                omit-xml-declaration="yes" />
    
    <xsl:template match="/value">
        INSERT INTO foo (<xsl:apply-templates select="//member" mode="name"/>) 
        VALUES (<xsl:apply-templates select="//member" mode="value"/>)
    </xsl:template>
    
    <xsl:template match="member" mode="name">
        <xsl:value-of select="name" />
        <xsl:if test="position()!=last()">,</xsl:if>
    </xsl:template>
    
    <xsl:template match="member" mode="value">
        <xsl:apply-templates select="value" />
        <xsl:if test="position()!=last()">,</xsl:if>
    </xsl:template>
    
    <xsl:template match="int">
        <xsl:value-of select="." />
    </xsl:template>
    
    <xsl:template match="string">
        '<xsl:value-of select="." />'
    </xsl:template>
    
</xsl:stylesheet>

Verwijderd

Waarom probeer je dat te voorkomen? :?

Denk je dat die logische tests zo lichtgewicht zijn?

Verwijderd

Topicstarter
Ik heb veel gepuzzeld, op het web gekeken (dit was wel aardig: http://www.zvon.org/xxl/X...General_dut/examples.html) en vooral de code van Genoil hierboven bestudeerd. Met zijn code kom ik het verst, maar er blijft een probleem dat ik niet kan oplossen, vermoedelijk doordat ik niet echt goed kan omgaan met XPath.

Dit is het resultaat van de code van Genoil:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
INSERT INTO foo (nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode,
nr_hotels,languagecode,name,city_id,countrycode) 
VALUES (1, 'en' , 'Aalsmeer' ,-2140138, 'nl' ,
1, 'en' , 'Aardenburg' ,-2140176, 'nl' ,
1, 'en' , 'Aduarderzijl' ,-2140316, 'nl' ,
2, 'en' , 'Alkmaar' ,-2140374, 'nl' ,
1, 'en' , 'Almere-Stad' ,-2140394, 'nl' ,
2, 'en' , 'Alphen aan den Rijn' ,-2140398, 'nl' ,
3, 'en' , 'Amersfoort' ,-2140452, 'nl' ,
2, 'en' , 'Amstelveen' ,-2140475, 'nl' ,
227, 'en' , 'Amsterdam' ,-2140479, 'nl' ,
1, 'en' , 'Anloo' ,-2140527, 'nl' );


(de linebreaks heb ik er zelf ingezet, eigenlijk is de output één regel)

Overigens wordt deze code nog een aantal keer geloopt, maar dan met andere waarden. Het probleem zit 'm in wat er na het eerste openingshaakje komt. Het is natuurlijk de bedoeling dat er vijf velden worden ingevoerd, namelijk nr_hotels, languagecode, name, city_id en countrycode. Echter, zoals je ziet, worden deze velden een paar keer geloopt binnen één SQL-statement en dat gaat niet werken.

Hoe los ik het op? Ik snap niet precies welk XPath-functie daarbij hoort. Ik snap het niet goed meer. :(

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Je moet dan de eerste apply-templates maar 1 keer toepassen.

Neem bijvoorbeeld deze input:
code:
1
2
3
4
5
6
<root>
  <fred>
    <foo>test 1</foo>
  </fred>
  <foo>test 2</foo>
</root>

Stel dat ik alleen de eerste foo wilt hebben in de output. Dan kan je het XPath (//foo)[1] gebruiken. Dit XPath verwijst alleen naar het eerste element van de node-set die het resultaat is van //foo.

Complete XSLT:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<xsl:transform version = "1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
  <xsl:template match="root">
    <result>
      <xsl:apply-templates select="(//foo)[1]"/>
    </result>
  </xsl:template>

  <xsl:template match="foo">
    <bar>
      <xsl:value-of select="text()"/>
    </bar>
  </xsl:template>
</xsl:transform>


Resultaat:
code:
1
2
3
4
<?xml version="1.0"?>
<result>
  <bar>test 1</bar>
</result>


Oldskool voorbeeld ;) .

[ Voor 40% gewijzigd door mbravenboer op 13-07-2004 16:08 ]

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

Topicstarter
Met dank aan mbravenboer kom ik alweer een heel stuk verder. :) Nu ontbreekt alleen nog het allerlaatste stukje. Het probleem nu, is dat de code alleen wordt toegepast op de eerste node-set. Ik snap niet hoe ik dat moet veranderen. Heb daar wel wat ideeën over, maar wanneer ik dat toepas, sloop ik alles of helpt het gewoon helemaal niet.

Nogmaals een stukje XML-code:
code:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="ISO-8859-1"?>
<methodResponse>
<params>
<param>
<value><array>
<data>
<value><struct>
<member><name>city_id</name>
<value><int>-2140138</int></value>
</member>
<member><name>countrycode</name>
<value><string>nl</string></value>
</member>
<member><name>name</name>
<value><string>Aalsmeer</string></value>
</member>
<member><name>nr_hotels</name>
<value><int>1</int></value>
</member>
<member><name>languagecode</name>
<value><string>en</string></value>
</member>
</struct></value>
<value><struct>
<member><name>city_id</name>
<value><int>-2140176</int></value>
</member>
<member><name>countrycode</name>
<value><string>nl</string></value>
</member>
<member><name>name</name>
<value><string>Aardenburg</string></value>
</member>
<member><name>nr_hotels</name>
<value><int>1</int></value>
</member>
<member><name>languagecode</name>
<value><string>en</string></value>
</member>
</struct></value>
</data>
</array></value>
</param>
</params>
</methodResponse>



Dit is de gebruikte XSL-code:
code:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version = "1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:output method="text" 
                indent="no"
                omit-xml-declaration="yes" />
    
    <xsl:template match="/methodResponse/params/param/value/array/data/value/struct">
        INSERT INTO foo 
        (<xsl:apply-templates select="(//member)[1]" mode="name"/>, 
        <xsl:apply-templates select="(//member)[2]" mode="name"/>,
        <xsl:apply-templates select="(//member)[3]" mode="name"/>,
        <xsl:apply-templates select="(//member)[4]" mode="name"/>,
        <xsl:apply-templates select="(//member)[5]" mode="name"/>)

        VALUES (<xsl:apply-templates select="(//member)[1]" mode="value"/>,
        <xsl:apply-templates select="(//member)[2]" mode="value"/>,
        <xsl:apply-templates select="(//member)[3]" mode="value"/>,
        <xsl:apply-templates select="(//member)[4]" mode="value"/>,
        <xsl:apply-templates select="(//member)[5]" mode="value"/>);
    </xsl:template>
    
    <xsl:template match="member" mode="name">
        <xsl:value-of select="name" />
        <xsl:if test="position()!=last()">,</xsl:if>
    </xsl:template>
    
    <xsl:template match="member" mode="value">
        <xsl:apply-templates select="value" />
        <xsl:if test="position()!=last()">,</xsl:if>
    </xsl:template>
    
    <xsl:template match="int">
        <xsl:value-of select="." />
    </xsl:template>
    
    <xsl:template match="string">
        '<xsl:value-of select="." />'
    </xsl:template>

</xsl:stylesheet>


En het resultaat vind je hier: http://www.rachid.nl/test/

Ik voel me momenteel heel erg dom, omdat ik waarschijnlijk iets heel kleins over het hoofd zie, maar ik kan er gewoon echt niet opkomen. :'( Wie ziet het wel? Thanks in advance, though. :Y)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Die positie voorwaarde (de [1]) moet je uiteraard alleen toepassen als je met de namen bezig bent. De waarden wil je juist wel allemaal doen.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Verwijderd

waarom niet gewoon twee keer een apply-templates op members. Dus zonder die [12345] dingen??

en dan wel
code:
1
<xsl:apply-templates select="member" mode="name"/>


doen

Verwijderd

Topicstarter
@mbravenboer:
Maar als ik dat doe, dan zorg ik er toch voor dat ik zoiets krijg als dit?

code:
1
2
3
4
5
6
INSERT INTO foo (nr_hotels, languagecode, name, city_id, countrycode)
VALUES (1, 'en' , 'Aalsmeer' ,-2140138, 'nl' ,1, 'en' , 'Aardenburg' ,-2140176, 'nl' ,
1, 'en' , 'Aduarderzijl' ,-2140316, 'nl' ,2, 'en' , 'Alkmaar' ,-2140374, 'nl' ,
1, 'en' , 'Almere-Stad' ,-2140394, 'nl' ,2, 'en' , 'Alphen aan den Rijn' ,-2140398, 'nl' ,
3, 'en' , 'Amersfoort' ,-2140452, 'nl' ,2, 'en' , 'Amstelveen' ,-2140475, 'nl' ,
227, 'en' , 'Amsterdam' ,-2140479, 'nl' ,1, 'en' , 'Anloo' ,-2140527, 'nl')


Dat kan ook niet helemaal kloppen, toch? Of begrijp ik je nou verkeerd? :)

Verwijderd

zie het probleem nu ook

ongetest:
code:
1
2
3
4
5
<xsl:template match="/methodResponse/params/param/value/array/data/value">
        INSERT INTO foo 
        (<xsl:apply-templates select="(/struct/member" mode="name"/>)
        VALUES (<xsl:apply-templates select="(/struct/member" mode="value"/>);
    </xsl:template>

Verwijderd

Topicstarter
Wauw iruoy, thanks! :D Je had een paar kleine syntaxfoutjes (een slash teveel en ergens een set haakjes niet afgesloten), maar ik heb nu exact wat ik wil!

Iedereen die bijgedragen heeft aan de totstandkoming van deze oplossing en dus aan het schrijven in deze post: thanks a lot! _/-\o_

Verwijderd

sorry dat ik zo'n domme vraag stel op welke platform moet je dit draaien of op wat voor webserver???

  • MisterData
  • Registratie: September 2001
  • Laatst online: 16-05 23:29
Je kunt een XML bestand omzetten naar XSLT met een browser (door een <?xml stylesheet-regeltje bovenaan je .xml te zetten), of je kunt deze 'transformatie' serverside laten plaatsvinden (met PHP4/5 of een andere scripttaal).
Pagina: 1