XML mit PHP parsen: Existenz eines Nodes mit Attributen überprüfen?

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Korodny
Beiträge: 705
Registriert: 09.09.2014 18:33:22
Lizenz eigener Beiträge: GNU Free Documentation License

XML mit PHP parsen: Existenz eines Nodes mit Attributen überprüfen?

Beitrag von Korodny » 16.07.2023 23:10:57

Ich versuche gerade, RSS-/Atom-Feeds in einem PHP-Script zu parsen. Ein Atom-Feed ist ein XML-Dokument, das für jeden Eintrag vereinfacht dargestellt so aussieht:

Code: Alles auswählen

  <entry>
    <title>TEOS - Commodore 64 multitasking char mode OS - Status #8</title>
    <link rel="alternate" href="https://yewtu.be/watch?v=ky5SZY4wHgc"/>
    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml"><p>Blablabla</p></div>
    </content>
    <published>2021-04-14T19:07:24+00:00</published>
  </entry>
Ich will prüfen, ob der "Content"-Node vorhanden ist, manche Feeds nutzen stattdessen nämlich <summary>.

Bei einem Node ohne Attribute geht das einfach mit isset(), etwas so:

Code: Alles auswählen

$currentFeed = new simpleXMLElement(file_get_contents($feedURL));

if(isset($currentFeed->entry->title) {
  ...
Das funktioniert problemlos. Die selbe Prüfung auf einen <content>-Node schlägt aber fehl: isset($currentFeed->entry->content) ist niemals wahr.

Ich vermute, das liegt an dem vorhanden Attribut für <content>? Jemand eine Idee?

thoerb
Beiträge: 1677
Registriert: 01.08.2012 15:34:53
Lizenz eigener Beiträge: MIT Lizenz

Re: XML mit PHP parsen: Existenz eines Nodes mit Attributen überprüfen?

Beitrag von thoerb » 17.07.2023 00:00:19

Korodny hat geschrieben: ↑ zum Beitrag ↑
16.07.2023 23:10:57
Jemand eine Idee?
Das könnte funktionieren:

Code: Alles auswählen

if(is_object($currentFeed->entry->content)) {
  ...
Zur Not schau es dir mal mit print_r an:

Code: Alles auswählen

print_r($currentFeed);

JTH
Moderator
Beiträge: 3023
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: XML mit PHP parsen: Existenz eines Nodes mit Attributen überprüfen?

Beitrag von JTH » 17.07.2023 11:10:05

Ich habs spaßeshalber mal ausprobiert. Funktionierte ohne Probleme, die Existenz eines Kindelements mit isset() zu prüfen.

Ich könnt mir vorstellen, dass du ignorierst/übersiehst, dass ein Atom-Feed mehrere <entry>s enthalten kann, Korodny? Denn isset($currentFeed->entry->content) würde – nehme ich nach Ausprobieren an, PHP ist laange her – immer nur im ersten Entry nach dem <content> gucken.

Das hier als Beispiel liefert die erwartete Ausgabe:

Code: Alles auswählen

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>xkcd.com</title>
  <link href="https://xkcd.com/" rel="alternate"/>
  <id>https://xkcd.com/</id>
  <updated>2023-07-14T00:00:00Z</updated>
  <entry>
    <title>Fireflies</title>
    <link href="https://xkcd.com/2802/" rel="alternate"/>
    <updated>2023-07-14T00:00:00Z</updated>
    <id>https://xkcd.com/2802/</id>
    <summary type="html">&lt;img src="https://imgs.xkcd.com/comics/fireflies.png" title="I feel bad for Earth 2 and their shadowflies." alt="I feel bad for Earth 2 and their shadowflies." /&gt;</summary>
  </entry>
  <entry>
    <title>Contact Merge</title>
    <link href="https://xkcd.com/2801/" rel="alternate"/>
    <updated>2023-07-12T00:00:00Z</updated>
    <id>https://xkcd.com/2801/</id>
    <content type="html">&lt;img src="https://imgs.xkcd.com/comics/contact_merge.png" title="I actually kind of feel like John and Surf King wouldn't like each other, which is a lot to unpack." alt="I actually kind of feel like John and Surf King wouldn't like each other, which is a lot to unpack." /&gt;</content>
  </entry>
</feed>

Code: Alles auswählen

<?php
$feed = new SimpleXMLElement(file_get_contents("xkcd.xml"));
foreach ($feed->getChildren() as $child) {
	if ($child->getName() !== "entry") {
		continue;
	}

	echo "Found an <entry> with <title> '" . $child->title . "'\n";

	if (isset($child->content)) {
		echo "Has <content>\n";
	}

	if (isset($child->summary)) {
		echo "Has <summary>\n";
	}
}

Code: Alles auswählen

$ php -f php.php
Found an <entry> with <title> 'Fireflies':
Has <summary>
Found an <entry> with <title> 'Contact Merge':
Has <content>
Manchmal bekannt als Just (another) Terminal Hacker.

Korodny
Beiträge: 705
Registriert: 09.09.2014 18:33:22
Lizenz eigener Beiträge: GNU Free Documentation License

Re: XML mit PHP parsen: Existenz eines Nodes mit Attributen überprüfen?

Beitrag von Korodny » 17.07.2023 13:05:53

JTH hat geschrieben: ↑ zum Beitrag ↑
17.07.2023 11:10:05
Ich könnt mir vorstellen, dass du ignorierst/übersiehst, dass ein Atom-Feed mehrere <entry>s enthalten kann, Korodny?
Nein, ich gehe schon mit foreach alle <entry>s der Reihe nach durch - ich hänge eine gekürzte Version meines Ansatzes mal unten an den Beitrag.

Aber dein Beispiel-XML - das auch mein Code problemlos parst - und der Hinweis von @thoerb, mal mit print_r zu "debuggen", haben klargemacht was das Problem ist: Der XKCD-Feed maskiert die HTML-Tags innerhalb der <content>- und <summary>-Nodes mit &lt; etc.. Die von mir bisher als Testobjekte benutzten Feeds (hauptsächlich Youtube, Beispiel-Link) machen das nicht.

Das bringt SimpleXML natürlich mächtig durcheinander - es interpretiert <div> und <p> als Child-Nodes von <content>

Die (neue) Frage wäre jetzt also: Wie maskiere ich sämtliche HTML-Entities in einem Node eines XML-Files, ohne die XML-Struktur zu beeinträchtigen? Ich gehe nach dem Mittagessen mal ein bisschen suchen...

Hier noch mein Ansatz zum Parsen der XML-Dateien:

Code: Alles auswählen

<?php

$currentFeed = new simpleXMLElement(file_get_contents("xkcd.xml"));

$feedTitle = $currentFeed->title;

foreach($currentFeed->entry as $currentItem) {

	if(isset($currentItem->published)) {
		$currentTime = $currentItem->published;
	} else {
		$currentTime = $currentItem->updated;
	}

	$currentTitle = $currentItem->title;
	$currentLink = $currentItem->link->attributes()->href;

	if(isset($currentItem->content)) {
		$currentDesc = $currentItem->content;
	} else {
		$currentDesc = $currentItem->summary;
	}

	echo $currentTitle . "\n" .
		$currentLink . "\n" .
		$currentTime . "\n" .
		$currentDesc . "\n" .
		"----------------------------\n";
}

?>

JTH
Moderator
Beiträge: 3023
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: XML mit PHP parsen: Existenz eines Nodes mit Attributen überprüfen?

Beitrag von JTH » 17.07.2023 15:36:30

Korodny hat geschrieben: ↑ zum Beitrag ↑
17.07.2023 13:05:53
Das bringt SimpleXML natürlich mächtig durcheinander - es interpretiert <div> und <p> als Child-Nodes von <content>
„Durcheinanderbringen“ würd implizieren, dass das eine falsche Verwendung ist, (X)HTML in dem Atom-XML einzubetten. Das ist es aber nicht. Es ist generell möglich und ein gewollter Einsatzzweck, verschiedene XML-Dokumenttypen ineinander einzubetten. Siehe z.B. das Beispiel im Wikipedia-Eintrag zu Atom, das ist genau der Fall mit <content> und enthaltenem XHTML. Das Atom-<content>-Element soll wohl sogar, wenn der content als type="xhtml" angegeben ist, ein <div> enthalten. Wenn du auf Nummer sicher gehen willst, könnte es nicht verkehrt sein, an der Stelle zwischen type=html und type=xhtml zu unterscheiden.

SimpleXML interpretiert das eingebettete XHTML dann eben auch zu Elementen und co. Wenn dich das stört, wäre die Frage, was genau du mit dem Inhalt der <content>- und <summary>-Elemente denn im Weiteren vorhast? Führt dich $currentItem->content/summary->asXML() oder so als Ansatz weiter?
Manchmal bekannt als Just (another) Terminal Hacker.

Antworten