Dieper in XML

Pagina: 1
Acties:

  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
Hallo allen,

ik ben bezig met een xml parse systeem op basis van SimpleXML. Nu moet ik hiervoor naast de max 1lvl diepe items ook diepere items in een bestaande XML toevoegen.

Hiervoor laad ik eerst de xml in, deze ziet er *(bijvoorbeeld) zo uit:
XML:
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<rweb_export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <root>
    <header>
      <creation>2009-10-11T09:30:47Z</creation>
      <type>type of xml export</type>
      <example> 
    </header>
  </root>


Goed, stel nu dat ik in example een extra item (child) wil toevoegen. Nou weet ik dat dit gaat met:
$xml_obj->header->example->addChild('value');

Echter weet ik wel altijd de naam van het element wat ik moet toevoegen en het parent element daarvan, in dit geval <header>, maar ik wil dynamisch dingen kunnen toevoegen. Dus wanneer ik zeg:
add_child_to('example', 'value'); dat die dan zelf bepaald dat dit in header->example staat, dus dat wanneer ik alleen 'example' meegeef aan de functie, dat er dan onderstaande gebeurt.

$xml_obj->header->example->addChild('value');


Ik kan evt. ook een array meegeven aan de functie, met daarin de parents in juiste volgorde (in dit geval array('header','example');) maar ook dan lukt het me niet om dit dynamisch te doen. Komt er nog een 3e parent bij dan moet deze automatisch zo worden opgemaakt dat er onderstaande komt:

$xml_obj->header->example->eenderde_element->addChild('value');

Hopelijk snapt iemand wat ik bedoel, ik weet het even niet.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

PHP:
1
2
3
$a->bla = 34;
$b = "bla";
echo $a->$b;

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Icelus
  • Registratie: Januari 2004
  • Niet online
Bedoel je dit?
PHP:
1
2
$child = 'example';
$xml_obj->header->{$child}->addChild('value');

Kijk ook 'ns naar XPath.

Developer Accused Of Unreadable Code Refuses To Comment


  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
@Icelus en .oisyn

Dat is ongeveer wat ik bedoel, maar stel nu dat ik dit kan hebben:

PHP:
1
2
$parents = array('header','example','detail','final');
$xml_obj->header->{$parents[0]}->{$parents[1]}->{$parents[2]}->{$parents[3]}->addChild('value');

dit zijn de items die van links naar rechts gezien (dus in de juiste volgorde) in de xml_obj moet worden aangeroepen. dus zo als hierboven.
Het kan ook zijn dat er maar 2 parents zijn (dus 2 lagen diep)
Hoe maak ik dit dynamisch, zonder dat ik steeds de diepte van het item moet controleren?

ik wil dus liever niet dit:
PHP:
1
2
3
4
5
6
7
8
9
10
11
<?php
if($diepte == 1)
{
  $xml_obj->header->{$parent[0]}->addChild('value'); 
}
elseif($diepte == 2)
{
 $xml_obj->header->{$parent[0]}->{$parent[1]}->addChild('value');  
}
?>
enz..


Dit moet toch makkeijker kunnen. Die xpath, hoe kan ik die gebruiken om evt. iets toe te voegen aan mijn gewenste element?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom wil je steeds bij de root beginnen? Je kunt toch gewoon de array aflopen waarin je steeds de huidige node bijhoudt?

PHP:
1
2
3
$a->aap->noot->mies = 34;
$b = $a->aap;
echo $b->noot->mies;

[ Voor 23% gewijzigd door .oisyn op 19-11-2009 16:34 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
Hoe bedoel je dat oisyn?
Het liefst wil ik alleen "final" meegeven uit het hierboven gemaakte voorbeeld. Hij moet dan zelf de het laatst voorkomende final element (zouden er ook 4 kunnen zijn binnen 1 parent) opzoeken en daar iets aan toevoegen.

XML:
1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<rweb_export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <root>
    <header>
      <creation>2009-10-11T09:30:47Z</creation>
      <type name='type of xml export' />
      <type name='another_type'/>
      <example> 
    </header>
  </root>


Op het moment dat ik hier bijv zeg:
add_child_to('type', 'name_of_added_child');
dat die dan zelf de laatsvoorkomende 'type' opzoekt en daar dit element aan toevoegd.
Alles wat ik tegen kom staat het volledig uitgeschreven en staan de elementen vast. Dat is hier niet het geval, dit moet dynamisch worden gegenereerd.
.oisyn schreef op donderdag 19 november 2009 @ 16:31:
Waarom wil je steeds bij de root beginnen? Je kunt toch gewoon de array aflopen waarin je steeds de huidige node bijhoudt?

PHP:
1
2
3
$a->aap->noot->mies = 34;
$b = $a->aap;
echo $b->noot->mies;
Maar de volgorde is niet altijd helemaal vastgesteld. Uitgaande van de hierboven ingeladen xml, wil ik dus eigenlijk simpel aangeven dat ik in type (hij moet zelf de laatst voorkomende daarvan bepalen) een nieuwe child toevoeg.
Ik heb even de bovenstaande xml aangepast. "type" heeft nu alleen even een attribuut.

[ Voor 26% gewijzigd door jbdeiman op 19-11-2009 16:41 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Waarom kom je dan met die voorbeelden die totaal niet bij je wensen aansluiten? Om dit aan te pakken sluit ik me aan bij Icelus. Als je niet weet waar je moet zijn moet je zoeken. En de manier om te zoeken is met XPath.

Een XPath query geeft je een lijst terug met nodes die aan je query voldoen. Die kun je aanpassen.

[ Voor 21% gewijzigd door .oisyn op 19-11-2009 16:44 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
Jah, srry. Ik weet het ook gewoon even niet. Vind het ook lastig te omschrijven. Ik snap alleen de werking van xpath in dit geheel niet, hoe moet ik daarmee zorgen dat ik het juiste element update?
Kan iemand een voorbeeldje geven/ vinden?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

Geen idee, nooit iets met XPath gedaan. Maar je snapt wel het feit dat de 'b' in $a->b ook gewoon een object is, en dat als je $b = $a->b doet, dat je dan met $b kunt werken alsof je $a->b bedoelt?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
@oisyn
dat weet ik, maar het is zo dat een xml string wordt gegenereerd uit een functie. Een andere functie hangt daar dan weer elementen onder etc. Het gaan om een vrij complex xml bestand.
Wat jij aangeeft zou wel kunnen, maar daar wordt het geheel weer onoverzichtelijk van. Vandaar dat ik dus op zoek ben naar een methode waarop dit veel gemakkelijker kan.

Een bepaald element (met een naam) kan meerdere keren voorkomen, maar altijd binnen eenzelfde header. Het kan hierbij echter wel zijn dat binnen het element "type" (die dus 2 keer voorkomt) ook in elk een nieuw type met dezelfde naam komt. Hij moet dus het laatste "type element" pakken op dat moment.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 17-09 14:05

.oisyn

Moderator Devschuur®

Demotivational Speaker

jbdeiman schreef op donderdag 19 november 2009 @ 16:51:
@oisyn
dat weet ik, maar het is zo dat een xml string wordt gegenereerd uit een functie. Een andere functie hangt daar dan weer elementen onder etc. Het gaan om een vrij complex xml bestand.
Dat doet er niet zoveel toe.
Wat jij aangeeft zou wel kunnen, maar daar wordt het geheel weer onoverzichtelijk van. Vandaar dat ik dus op zoek ben naar een methode waarop dit veel gemakkelijker kan.
Nee, wat ik zeg is dat je XPath moet gebruiken die elementen teruggeeft die je naar je gerieven kunt aanpassen. Als je wilt weten hoe XPath werkt moet je even naar een tutorial googlen. Want met XPath is het mogelijk om te zeggen: "zoek het element genaamd 'x' wat een child is van element 'y'", en dat lijkt me precies wat je wilt.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • TaraWij
  • Registratie: December 2007
  • Laatst online: 08-02 18:37
Tenzij je een back-end database systeem gebruikt zoals Sedna denk ik niet dat je zo maar direct een query kan doen die een bepaalt element achter een andere bepaald element kan plaatsen. Wat je wel zou kunnen doen is vanuit het huidige document een nieuw document genereren en het er tussen plaatsen.

Met document opnieuw aanmaken:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<rweb_export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <root>
    <header>
      <creation>2009-10-11T09:30:47Z</creation>
      <type>type of xml export</type>
      <type>another_type</type>
      <type>name_of_added_child</type>
      <example /> 
    </header>
  </root>
</rweb_export>


De node type aan de node header toevoegen:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<rweb_export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <root>
    <header>
      <creation>2009-10-11T09:30:47Z</creation>
      <type>type of xml export</type>
      <type>another_type</type>
      <example /> 
      <type>name_of_added_child</type>
    </header>
  </root>
</rweb_export>


Voor je verder gaat raad ik je wel even aan je te verdiepen in de nodige artikels om handig met XML overweg te kunnen, je spaart je heel wat trial-and-error tijd uit als je een beter begrip hebt van de mogelijk technieken. De voornaamste:

W3Schools (XQuery en XSL voor als je nog geadvanceerder wil gaan)
XML
XML Document Object Model
XPath (XML Nodes opvragen)
XQuery (Queries doen die niet met XPath kunnen)
XSL (XML Documenten Omvormen)

PHP.net
PHP 5 - Document Object Model

SimpleXML kan wel wat, maar als je echt alles wil kunnen moet je eens door bovenstaande link kijken. Wanneer je ook XML DOM hebt doorgelezen zou je zo aan slag moeten kunnen.

Om een node test toe te voegen aan de node example:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
// Laad $xml uit een string, er zijn ook functies voor files.
$dom = new DOMDocument();
$dom->loadXML($xml);

// Geeft alle elementen met de naam example.
$xpath = new DOMXPath($dom);
$examples = $xpath->query("//example");

// Voegt aan de eerste example node <test>value</test> toe.
if ($examples->length > 0)
  $examples->item(0)->appendChild($dom->createElement("test", "value"));
?>


Resultaat:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<rweb_export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <root>
    <header>
      <creation>2009-10-11T09:30:47Z</creation>
      <type>type of xml export</type>
      <type>another_type</type>
      <example>
        <test>value</test>
      </example>
    </header>
  </root>
</rweb_export>


(rweb_export en example waren niet afgesloten)

  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
@oisyn
Dat is nou juist het probleem, zie example 1 hier:

http://www.php.net/manual/en/simplexmlelement.xpath.php

Hij returned de waarden, maar ik wil iets toevoegen aan /type bijvoorbeeld. Via xpath krijg ik de value en niet een manier om daar een extra element in toe te voegen.
Daarvoor moet ik nu steeds die hele boel uitwerken.

@oisyn
Een deel van het xml bestand wordt gegenereerd. Ergens anders (dit xml bestand kan opgeslagen zijn geweest en weer ingelezen, dus die $a->b->c; $b= $a->b; enzo werkt niet in deze. (misschien had ik ook eerder dit moeten aangeven dat het steeds ingelezen wordt of kan worden)

En ik wil ook niet hetgeen doen wat ik hierboven heb gedaan met elk element benoemen, tenminste niet handmatig met een teller voor het aantal elementen en daar vanuit die array dan de juiste aanroep van maken.
Dus niet dit gebeuren:
PHP:
1
2
3
4
5
6
7
8
9
10
11
<?php
if($diepte == 1)
{
  $xml_obj->header->{$parent[0]}->addChild('value'); 
}
elseif($diepte == 2)
{
 $xml_obj->header->{$parent[0]}->{$parent[1]}->addChild('value');  
}
?>
enz..


get the point? Via xpath lijk ik alleen maar de values terug te krijgen.

  • TaraWij
  • Registratie: December 2007
  • Laatst online: 08-02 18:37
jbdeiman schreef op donderdag 19 november 2009 @ 17:05:
@oisyn
Hij returned de waarden, maar ik wil iets toevoegen aan /type bijvoorbeeld. Via xpath krijg ik de value en niet een manier om daar een extra element in toe te voegen.
Daarvoor moet ik nu steeds die hele boel uitwerken.
Zie mijn voorgaande post (kan wel eens overgeslagen worden als je reageert en niet terugscrollt), SimpleXML is voor wat jij wilt doen te simpel. Met het voorbeeld dat ik je gaf krijg je wel degelijk een lijst van nodes terug van XPath, en deze zijn ook gewoon aanpasbaar.

  • jbdeiman
  • Registratie: September 2008
  • Laatst online: 22:37
@TomWij
Dankje voor je reactie.. ik las hem net en verbaasde me er eerst over dat mijn eigen post er niet stond.. :P maar jij was dus tussendoor.
Heb het er onderweg in de trein nog eens over gehad. We gaan inderdaad met domxml aan de slag, dan hebben we veel meer en betere mogelijkheden. SimpleXML lijkt zo simpel, maar zogauw je wat meer wilt is het niet simpel meer.

Allemaal bedankt voor het meedenken.
Pagina: 1