[php] replace element in xml adhv ander element

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 15:58

Saven

Administrator

Topicstarter
Yo devvers,

Ik zit met een probleem waarvan ik voor het eerst niet precies weet hoe ik dit (het beste, if all) kan oplossen. Ik heb een (externe) XML feed. Ik ga werken met voorbeelddata, maar het principe komt op hetzelfde neer

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<rss xmlns:x="http://......./" version="2.0">
    <lijst>
        <item>
            <x:id>1234</x:id>
            <titel>Banaan rood</titel>
            <x:image>banaan.jpg</x:image>
        </item>
        <item>
            <x:id>456</x:id>
            <titel>Wiel</titel>
            <x:image>wieltje.jpg</x:image>
        </item>
        <item>
            <x:id>7890</x:id>
            <titel>Michael Jackson</titel>
            <x:image>mj.jpg</x:image>
        </item>
    </lijst>
</rss>


Ik wil per item de image vervangen van een bepaalde id. Ik heb dus de id, en de bijbehorende (nieuwe) image.

Voorbeeld:
PHP:
1
2
3
4
5
6
7
8
9
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$feed = simplexml_load_file($this->externalUrl);
//... en nu?


Maar wat is nu praktisch? Ik moet eerlijk bekennen dat ik niet helemaal thuis ben in de XML wereld, maar heb wel gekeken of ik eventueel de inhoud van een element kan replacen. Dat kon volgens mij al niet.

Een andere optie was wellicht de feed als string ophalen, en dan een preg_replace. Echter, de feed kan zomaar 10.000 of meer items bevatten die replaced moeten worden. Dan wordt dat een beetje een 'dure' aangelegenheid.

Zelf een XML feed genereren is ook niet echt een optie omdat ik niet alle data heb helaas. Enkel een ID en de nieuwe image.

[ Voor 5% gewijzigd door Saven op 17-08-2015 23:18 ]


Acties:
  • 0 Henk 'm!

  • Pizzalucht
  • Registratie: Januari 2011
  • Laatst online: 20:31

Pizzalucht

Snotneus.

Ik denk dat het parsen van een XML file met 10.000 items in PHP met simplexml en er vervolgens doorheen loopen 'duurder' is dan een simple preg_replace of str_replace dus als je het daarmee kan doen zou ik dat doen.

Als ik je voorbeeld zo zie kom je niet weg met een simpele preg_replace of str_replace, maar met preg_replace_callback zou je wel een heel eind kunnen komen.

[ Voor 6% gewijzigd door Pizzalucht op 17-08-2015 23:54 ]


Acties:
  • 0 Henk 'm!

  • azerty
  • Registratie: Maart 2009
  • Laatst online: 18:27
Je zou naar XPath kunnen kijken om alle nodes te selecteren die je moet vervangen, en dan de vervanging uitvoeren... Geen idee hoe 'duur' dat zou zijn, maar lijkt mij een vrij nette oplossing :)

Een XPath die je node met de te vervangen afbeelding voor id 7890 oplevert is:

//x:id[text()='7890']/following-sibling::*[2]

Dan kun je (afhankelijk natuulrijk van hoeveel vervangingen je moet doen) in een loopje de XPath expressies maken, uitvoeren en de vervangingen doen :)

Edit: http://php.net/manual/en/simplexmlelement.xpath.php, voor wat leesvoer ivm xpath + simplexml :)

Edit 2: http://php.net/manual/en/simplexml.examples-basic.php, hier kun je wel een voorbeeldje vinden hoe je data in simpleXML wijzigt.

[ Voor 19% gewijzigd door azerty op 18-08-2015 00:23 ]


Acties:
  • 0 Henk 'm!

  • Douweegbertje
  • Registratie: Mei 2008
  • Laatst online: 20:54

Douweegbertje

Wat kinderachtig.. godverdomme

Je zult moeten loopen door je XML, vervolgens roep je de attribute aan zoals

code:
1
xml_attribute($item, 'image') = 'waarde';


En vervolgens kun je hem vervangen.


Ik zou gewoon hem wel in een XML parser gooien, zodat het netjes een XML bericht blijft en je veel meer opties hebt. Verder is een xpath search ook een prima oplossing.

Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 02:21

Janoz

Moderator Devschuur®

!litemod

Als het echt veel XML is, dan is het misschien handiger om het streaming te parsen met de sax parser.

Wat ga je uiteindelijk met het resultaat doen?

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 15:58

Saven

Administrator

Topicstarter
Thanks voor jullie reacties. Er is dus geen 'hele makkelijke' manier? ;(
Douweegbertje schreef op dinsdag 18 augustus 2015 @ 08:22:
Je zult moeten loopen door je XML, vervolgens roep je de attribute aan zoals

code:
1
xml_attribute($item, 'image') = 'waarde';


En vervolgens kun je hem vervangen.


Ik zou gewoon hem wel in een XML parser gooien, zodat het netjes een XML bericht blijft en je veel meer opties hebt. Verder is een xpath search ook een prima oplossing.
Dat klinkt wel iets als wat ik nog snap :P Even induiken. Dit lijkt alleen geen standaard PHP-functie?
Janoz schreef op dinsdag 18 augustus 2015 @ 09:40:
Als het echt veel XML is, dan is het misschien handiger om het streaming te parsen met de sax parser.

Wat ga je uiteindelijk met het resultaat doen?
sax parser moet ik even uitzoeken, het zijn straks meerdere feeds, en de items kunnen varieren van 100 tot 10.000 items. Het uiteindelijke resultaat wordt opgeslagen in een nieuw XML bestand. En gaat straks elke nacht een cron draaien die alle feeds update.

Acties:
  • 0 Henk 'm!

  • Douweegbertje
  • Registratie: Mei 2008
  • Laatst online: 20:54

Douweegbertje

Wat kinderachtig.. godverdomme

Saven schreef op dinsdag 18 augustus 2015 @ 10:48:
Thanks voor jullie reacties. Er is dus geen 'hele makkelijke' manier? ;(


[...]

Dat klinkt wel iets als wat ik nog snap :P Even induiken. Dit lijkt alleen geen standaard PHP-functie?


[...]

sax parser moet ik even uitzoeken, het zijn straks meerdere feeds, en de items kunnen varieren van 100 tot 10.000 items. Het uiteindelijke resultaat wordt opgeslagen in een nieuw XML bestand. En gaat straks elke nacht een cron draaien die alle feeds update.
xml_attribute is onderdeel van simplexml

http://php.net/manual/en/simplexmlelement.attributes.php

[ Voor 4% gewijzigd door Douweegbertje op 18-08-2015 11:25 ]


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 15:58

Saven

Administrator

Topicstarter
Heb even zitten kijken met xpath, maar kom hier niet uit:

PHP:
1
2
3
4
5
6
7
8
9
                $feed = @simplexml_load_file($this->externalUrl)

                foreach($replaces as $id => $image)
                {
                    foreach( $feed->xpath('/item[x:id='.$id.']') as $element )
                    {
                        echo 'replace '.$id.' with '.$image.'<br />';
                    }
                }

Elk pad dat ik probeer, met of zonder root element lijkt hij niet te pakken. Zie ik iets over het hoofd?
Anders toch even hier naar kijken :)

Acties:
  • 0 Henk 'm!

  • Douweegbertje
  • Registratie: Mei 2008
  • Laatst online: 20:54

Douweegbertje

Wat kinderachtig.. godverdomme

Nja je loopt in een loop op je root.

Lijkt me logischer dat je

PHP:
1
2
// foreach( $feed->xpath('/item[x:id='.$id.']') as $element )
 foreach( $image->xpath('/item[x:id='.$id.']') as $element )
gaat doen.

Los daarvan: debug gewoon eens, print_r even op $image, kijk wat er in zit. Dan weet je op welk element je iets moet zoeken en of je data klopt.

Daarbij zoek je nu het ID op het ID, terwijl ik juist het ID zou zoeken op basis van de array met de ID's die vervangen moeten worden.


In kort: iets meer debuggen, uitprinten van informatie en goed kijken hoe je geneste objecten in elkaar zitten en wanneer je wel en niet iets moet aanroepen en waarop. Bekijk ook de documentatie even goed, en zoek voorbeelden op.

[ Voor 24% gewijzigd door Douweegbertje op 18-08-2015 12:52 ]


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Goed, je moet even wat harder studeren maar, hiermee gaat het je lukken: http://php.net/XMLReader

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 15:58

Saven

Administrator

Topicstarter
Douweegbertje schreef op dinsdag 18 augustus 2015 @ 12:50:
Nja je loopt in een loop op je root.

Lijkt me logischer dat je

PHP:
1
2
// foreach( $feed->xpath('/item[x:id='.$id.']') as $element )
 foreach( $image->xpath('/item[x:id='.$id.']') as $element )
gaat doen.

Los daarvan: debug gewoon eens, print_r even op $image, kijk wat er in zit. Dan weet je op welk element je iets moet zoeken en of je data klopt.

Daarbij zoek je nu het ID op het ID, terwijl ik juist het ID zou zoeken op basis van de array met de ID's die vervangen moeten worden.


In kort: iets meer debuggen, uitprinten van informatie en goed kijken hoe je geneste objecten in elkaar zitten en wanneer je wel en niet iets moet aanroepen en waarop. Bekijk ook de documentatie even goed, en zoek voorbeelden op.
Thanks.

Hmm, ik zoek inderdaad op het id, omdat ik zegmaar zoiets wil:
PHP:
1
2
3
4
5
//foreach door die xml
if( array_key_exists($xid, $replaces) ) //$replaces = [ id => new_image ]
{
    replace_de_oude_image_met( $replaces[$xid] );
}


Overigens heb ik nu dit:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$feed = simplexml_load_file($this->externalUrl);

            if ($feed !== false)
            {
                foreach($feed->channel->item as $entry)
                {
                    $namespaces = $entry->getNameSpaces(true);
                    $g =  $entry->children($namespaces['g']);

                    if( array_key_exists((int)$g->id, $replaces) )
                    {
                        $oldNew[ (string)$g->image_link ] = $replaces[ (int)$g->id ];
                    }
                }

                print_r($oldNew);
            }

Werkt wel, maar of het mooi is? :') Hierna moet ik namelijk nog str_replaces gaan uitvoeren

[ Voor 5% gewijzigd door Saven op 18-08-2015 12:59 ]


Acties:
  • 0 Henk 'm!

  • azerty
  • Registratie: Maart 2009
  • Laatst online: 18:27
Waarom zou je nog str_replaces gaan uitvoeren?

Acties:
  • 0 Henk 'm!

  • Douweegbertje
  • Registratie: Mei 2008
  • Laatst online: 20:54

Douweegbertje

Wat kinderachtig.. godverdomme

Saven schreef op dinsdag 18 augustus 2015 @ 12:57:
[...]

Thanks.

Hmm, ik zoek inderdaad op het id, omdat ik zegmaar zoiets wil:
PHP:
1
2
3
4
5
//foreach door die xml
if( array_key_exists($xid, $replaces) ) //$replaces = [ id => new_image ]
{
    replace_de_oude_image_met( $replaces[$xid] );
}


Overigens heb ik nu dit:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$feed = simplexml_load_file($this->externalUrl);

            if ($feed !== false)
            {
                foreach($feed->channel->item as $entry)
                {
                    $namespaces = $entry->getNameSpaces(true);
                    $g =  $entry->children($namespaces['g']);

                    if( array_key_exists((int)$g->id, $replaces) )
                    {
                        $oldNew[ (string)$g->image_link ] = $replaces[ (int)$g->id ];
                    }
                }

                print_r($oldNew);
            }

Werkt wel, maar of het mooi is? :') Hierna moet ik namelijk nog str_replaces gaan uitvoeren
Nou het punt is dat je wat hebt geleerd en zelf even hebt nagedacht om tot überhaupt een oplossing te komen.

Zie hier hoe ik het zou doen (afhankelijk van het aantal data e.d.)


PHP:
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
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$data = simplexml_load_file('test.xml');

foreach ($replaces as $key => $value) 
{
    $result = array();
    $result = $data->xpath('//item[x:id="'.$key.'"]');
        

    if(!empty($result))
    {
        $result[0]->titel =  $replaces[$key];
    }
}




print_r($data);


Je moet alleen nog een for loop zetten bij de result, in het geval hij meerdere items vindt :)

Acties:
  • 0 Henk 'm!

  • azerty
  • Registratie: Maart 2009
  • Laatst online: 18:27
Douweegbertje schreef op dinsdag 18 augustus 2015 @ 13:45:
[...]


Nou het punt is dat je wat hebt geleerd en zelf even hebt nagedacht om tot überhaupt een oplossing te komen.

Zie hier hoe ik het zou doen (afhankelijk van het aantal data e.d.)


PHP:
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
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$data = simplexml_load_file('test.xml');

foreach ($replaces as $key => $value) 
{
    $result = array();
    $result = $data->xpath('//item[x:id="'.$key.'"]');
        

    if(!empty($result))
    {
        $result[0]->titel =  $replaces[$key];
    }
}




print_r($data);


Je moet alleen nog een for loop zetten bij de result, in het geval hij meerdere items vindt :)
Is inderdaad ook ongeveer hoe ik het zou doen (en de richting die Saven uit moet), maar met de Xpath die ik had had je het element direct :)

Acties:
  • 0 Henk 'm!

  • Pizzalucht
  • Registratie: Januari 2011
  • Laatst online: 20:31

Pizzalucht

Snotneus.

Douweegbertje schreef op dinsdag 18 augustus 2015 @ 13:45:
[...]


Nou het punt is dat je wat hebt geleerd en zelf even hebt nagedacht om tot überhaupt een oplossing te komen.

Zie hier hoe ik het zou doen (afhankelijk van het aantal data e.d.)


PHP:
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
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$data = simplexml_load_file('test.xml');

foreach ($replaces as $key => $value) 
{
    $result = array();
    $result = $data->xpath('//item[x:id="'.$key.'"]');
        

    if(!empty($result))
    {
        $result[0]->titel =  $replaces[$key];
    }
}




print_r($data);


Je moet alleen nog een for loop zetten bij de result, in het geval hij meerdere items vindt :)
Ligt er een beetje aan of $replaces ook veel data bevat of juist heel weinig, ik weet het niet zeker maar denk dat xpath trager is dan gewoon door alle items loopen en dan een:

PHP:
1
2
3
if (isset($replaces[$id])) {
  $item->titel =  $replaces[$id];
}


Maar goed, bij 10.000 items lijkt me dat nog steeds verwaarloosbaar.

Acties:
  • 0 Henk 'm!

  • emnich
  • Registratie: November 2012
  • Niet online

emnich

kom je hier vaker?

Het hangt inderdaad af van de hoeveelheid items in de feed vs het aantal te vervangen items welke aanpak je kiest. Als de XML wat groter wordt en je kiest voor de xpath oplossing dan moet je, als je kan, voorkomen dat je // gebruikt maar in dit voorbeeld gewoon /rss/lijst/item gebruiken ipv //item/

Acties:
  • 0 Henk 'm!

  • Douweegbertje
  • Registratie: Mei 2008
  • Laatst online: 20:54

Douweegbertje

Wat kinderachtig.. godverdomme

Saven schreef op dinsdag 18 augustus 2015 @ 12:57:


Overigens heb ik nu dit:

PHP:
1
                    if( array_key_exists((int)$g->id, $replaces) )
Met dingen zoals dat moet je wel oppassen. Wat nu als het ID een string is? Dan ga je opeens zoeken op een lege array key. Dat kan apart behaviour opleveren in je script. Wat is de reden dat je aan type juggling doet? Het is wel goed dat je nadenkt over data types, maar wat maakt het uit voor een array key? Of het nu string of int of w/e is, als hij matched met je array dan is het goed!
Wil je casten, ook prima.. maar handel dan wel je data af voordat je er mee verder gaat.

Acties:
  • 0 Henk 'm!

  • Saven
  • Registratie: December 2006
  • Laatst online: 15:58

Saven

Administrator

Topicstarter
Thanks guys, sorry voor late reply :P
DJMaze schreef op dinsdag 18 augustus 2015 @ 12:55:
Goed, je moet even wat harder studeren maar, hiermee gaat het je lukken: http://php.net/XMLReader
Ging m niet worden helaas :P
Douweegbertje schreef op dinsdag 18 augustus 2015 @ 13:45:
[...]


Nou het punt is dat je wat hebt geleerd en zelf even hebt nagedacht om tot überhaupt een oplossing te komen.

Zie hier hoe ik het zou doen (afhankelijk van het aantal data e.d.)


PHP:
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
$replaces = array
(
    1234 => 'banaanw8rsdf.jpg',
    567  => 'wielxzxc495sd.jpg',
    7890 => 'michaeltigeh489fns3.jpg',
);

$data = simplexml_load_file('test.xml');

foreach ($replaces as $key => $value) 
{
    $result = array();
    $result = $data->xpath('//item[x:id="'.$key.'"]');
        

    if(!empty($result))
    {
        $result[0]->titel =  $replaces[$key];
    }
}




print_r($data);


Je moet alleen nog een for loop zetten bij de result, in het geval hij meerdere items vindt :)
Zo doe ik het nu idd, leek me het meest praktisch idd. Is overigens uniek, dus een foreach hoeft niet :) Enige wat ik overigens wel doe:
PHP:
1
$result[0]->{'x:image'} = $image;

Of is daar een nettere manier voor dan met die brackets? :+
emnich schreef op dinsdag 18 augustus 2015 @ 14:17:
Het hangt inderdaad af van de hoeveelheid items in de feed vs het aantal te vervangen items welke aanpak je kiest. Als de XML wat groter wordt en je kiest voor de xpath oplossing dan moet je, als je kan, voorkomen dat je // gebruikt maar in dit voorbeeld gewoon /rss/lijst/item gebruiken ipv //item/
Goede tip, thanks
Douweegbertje schreef op dinsdag 18 augustus 2015 @ 15:00:
[...]


Met dingen zoals dat moet je wel oppassen. Wat nu als het ID een string is? Dan ga je opeens zoeken op een lege array key. Dat kan apart behaviour opleveren in je script. Wat is de reden dat je aan type juggling doet? Het is wel goed dat je nadenkt over data types, maar wat maakt het uit voor een array key? Of het nu string of int of w/e is, als hij matched met je array dan is het goed!
Wil je casten, ook prima.. maar handel dan wel je data af voordat je er mee verder gaat.
Hehe, id is altijd een int, maar kreeg een boel errors ofzo, dus toen even snel voor deze 'snelle' oplossing gekozen.
Pagina: 1