2012-12-20 21 views
5

Ich schreibe einen RSS-Feed für Google-Produkte mit SimpleXML in PHP. Ich habe meine Produkte kommen aus der Datenbank und erstellen die RSS-Datei in Ordnung, aber Probleme, wenn es um Namespaces kommt.So verwenden Sie Namespaces beim Schreiben von XML-Dateien mit SimpleXML

Ich habe Stack-Überlauf gegoogelt und suche und dutzende Beiträge von stoßen, wie XML zu analysieren Feeds Namespaces enthalten, aber mein Problem ist eigentlich Authoring eine XML-Datei mit einem Namespace. Hier

ist, was die Datei sollte wie folgt aussehen:

<?xml version="1.0" encoding="UTF-8" ?> 
<rss version ="2.0" xmlns:g="http://base.google.com/ns/1.0"> 
    <!-- content --> 
</rss> 

Und hier ist mein Code:

<?php 

$xml = new SimpleXMLElement('<rss></rss>'); 
$xml->addAttribute('version', '2.0'); 

$xml->addChild('channel'); 
$xml->channel->addChild('title', 'Removed'); 
$xml->channel->addChild('description', 'Removed'); 
$xml->channel->addChild('link', 'Removed'); 

foreach ($products as $product) { 
    $item = $xml->channel->addChild('item'); 
    $item->addChild('title', htmlspecialchars($product['title'])); 
    $item->addChild('description', htmlspecialchars($product['title'])); 
    $item->addChild('link', $product['url']); 
    $item->addChild('id', $product['product_id']); 
    $item->addChild('price', $product['price_latest']); 
    $item->addChild('brand', $product['range']); 
    $item->addChild('condition', 'new'); 
    $item->addChild('image_link', $product['image']); 
} 

Wie stelle ich den g Namespace, sowohl die xmlns Deklaration in der Wurzel rss Element und dann als Präfix für id, price, brand, condition und image_link in jedem item Element? Hier

+0

ich mit sehr empfehlen [DOM] (http://php.net/manual/en/book .dom.php) dafür. SimpleXML ist nur das - * einfach *. Was Sie tun, hat jetzt das Reich des Einfachen verlassen. Sie benötigen hierfür eine API mit allen Funktionen. – DaveRandom

+0

Haben Sie ein Beispiel dafür, wie ich einen Namespace mit DOM hinzufügen würde? –

+0

@MartinBean Es gibt ein Beispiel in [Handbuch für DOMDocument createElementNS] (http://php.net/manual/domdocument.createelements.php). – Furgas

Antwort

6

ist ein Beispiel dafür, wie dies zu tun mit DOM:

<?php 

    $nsUrl = 'http://base.google.com/ns/1.0'; 

    $doc = new DOMDocument('1.0', 'UTF-8'); 

    $rootNode = $doc->appendChild($doc->createElement('rss')); 
    $rootNode->setAttribute('version', '2.0'); 
    $rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:g', $nsUrl); 

    $channelNode = $rootNode->appendChild($doc->createElement('channel')); 
    $channelNode->appendChild($doc->createElement('title', 'Removed')); 
    $channelNode->appendChild($doc->createElement('description', 'Removed')); 
    $channelNode->appendChild($doc->createElement('link', 'Removed')); 

    foreach ($products as $product) { 
     $itemNode = $channelNode->appendChild($doc->createElement('item')); 
     $itemNode->appendChild($doc->createElement('title'))->appendChild($doc->createTextNode($product['title'])); 
     $itemNode->appendChild($doc->createElement('description'))->appendChild($doc->createTextNode($product['title'])); 
     $itemNode->appendChild($doc->createElement('link'))->appendChild($doc->createTextNode($product['url'])); 
     $itemNode->appendChild($doc->createElement('g:id'))->appendChild($doc->createTextNode($product['product_id'])); 
     $itemNode->appendChild($doc->createElement('g:price'))->appendChild($doc->createTextNode($product['price_latest'])); 
     $itemNode->appendChild($doc->createElement('g:brand'))->appendChild($doc->createTextNode($product['range'])); 
     $itemNode->appendChild($doc->createElement('g:condition'))->appendChild($doc->createTextNode('new')); 
     $itemNode->appendChild($doc->createElement('g:image_link'))->appendChild($doc->createTextNode($product['image'])); 
    } 

    echo $doc->saveXML(); 

See it working

+1

Danke, Dave. Perfekt! Es ist mir gerade gelungen, eine Arbeitslösung zu schreiben, die der Deinen sehr ähnlich ist: https://gist.github.com/4344973 –

+0

@MartinBean Cool :-) Nur ein echter Unterschied zwischen Dir und mir ist, dass ich explizite Textknoten-Erstellung für die Dynamik verwende Werte, die (IMHO) der beste Weg ist, um entweichende Entitäten zu behandeln, weil Sie die rohen Daten übergeben können und es wird automatisch maskiert, ohne mit 'htmlspecialchars()' herumzuspielen. Allerdings gebe ich zu, dass es * noch mehr * ausschweifend ist - DOM ist keine prägnante API, aber es ist die flexibelste, die es gibt. – DaveRandom

+0

Gut zu wissen, da ich tatsächlich den Produkttitel usw. in 'htmlspecialchars()' im eigentlichen Code eingepackt habe! Ich ersetze das durch die Methode des expliziten Erstellens eines Textknotens, wie Sie es getan haben. –

9

Diese kann getan worden, um die SimpleXMLElement Schnittstelle. IMHO das ist ein Hack der dodgiest Reihenfolge, aber es funktioniert für jetzt.

Der entscheidende Punkt ist, dass es wie folgt funktioniert, für jetzt, aber möglicherweise nicht weiter zu arbeiten. Daher empfehle ich dies in keiner Weise über die akzeptierte Antwort von @DaveRandom. Stattdessen füge ich diese Antwort hier ein, damit andere dies in Zukunft lesen und sich Zeit für die Suche nach SimpleXMLElement sparen können und einfach DaveRandoms DOM-basierten Ansatz verwenden :-)

Sie können "Trick "Der Parser soll verhindern, dass Ihr g: Namespacepräfix von Ihren Elementnamen abfällt, indem Sie ein" Müll "-Präfix vor Ihr echtes setzen, zB "blah:g:condition".

Ich habe Variationen dieser Antwort für die Verwendung in Attributpräfixen, aber nicht für Elementpräfixe gesehen. Und alle scheinen zu empfehlen, "xmlns:yourPrefix:yourAttribute" zu verwenden und den voll qualifizierten Namensraum als dritten Parameter zu übergeben, wobei in Wirklichkeit (zumindest von meinen eigenen persönlichen Tests) der xmlns: Teil so ziemlich alles sein kann (einschließlich Leerraum!) Vor einem Doppelpunkt :, aber es muss etwas vor diesem ersten Doppelpunkt sein, dh ":g:condition" wird nicht funktionieren. Und wenn Sie nicht tatsächlich einen Knoten erstellen, der den Namespace und das Präfix deklariert, wäre das gerenderte XML ungültig (dh das Namespace-Präfix, das Sie gegen Ihre Elemente hacken, hat keine Deklaration).

Also, basierend auf Ihrem ursprünglichen Code, würden Sie Folgendes tun. Beachten Sie auch das explizite Hinzufügen des Namespace in der Root-Node-Deklaration (obwohl dies wahrscheinlich über die API erfolgen kann - aber warum?).

$xml = new SimpleXMLElement('<rss xmlns:g="http://base.google.com/ns/1.0" />'); // May as well chuck the google ns in the root element declaration here, while we're at it, rather than adding it via a separate attribute. 
$xml->addAttribute('version', '2.0'); 
// $xml->addAttribute('hack:xmlns:g','http://base.google.com/ns/1.0'); //Or could do this instead... 

$xml->addChild('channel'); 
$xml->channel->addChild('title', 'Removed'); 
$xml->channel->addChild('description', 'Removed'); 
$xml->channel->addChild('link', 'Removed'); 

foreach ($products as $product) { 
    $item = $xml->channel->addChild('item'); 
    $item->addChild('title', htmlspecialchars($product['title'])); 
    $item->addChild('description', htmlspecialchars($product['title'])); 
    $item->addChild('link', $product['url']); 
    $item->addChild('hack:g:id', $product['product_id']); 
    $item->addChild('hack:g:price', $product['price_latest']); 
    $item->addChild('hack:g:brand', $product['range']); 
    $item->addChild('hack:g:condition', 'new'); 
    $item->addChild('hack:g:image_link', $product['image']); 
} 
+1

Sie haben meine Zeit gerettet. Danke Mann :) – nit3ch

+0

Vielen Dank, vermuch! –

1

Dies kann zusammen mit Hinzufügen des Namespace in die addChild Funktion, indem Sie die Namespace-Deklaration in die Root-Elemente Schöpfung erfolgen.

Die Änderung zum ursprünglichen Code:

$products[] = array(
    "title" => "Foobar", 
    "description" => "Foo", 
    "url" => "https://f.oo.bar", 
    "product_id" => "00001", 
    "price_latest" => "$3.50", 
    "range" => "Foo", 
    "image" => "https://f.oo.bar/image.tiff", 
); 

$xml = new SimpleXMLElement('<rss xmlns:g="http://base.google.com/ns/1.0"/>'); 
$xml->addAttribute('version', '2.0'); 

$xml->addChild('channel'); 
$xml->channel->addChild('title', 'Removed'); 
$xml->channel->addChild('description', 'Removed'); 
$xml->channel->addChild('link', 'Removed'); 

foreach ($products as $product) { 
    $item = $xml->channel->addChild('item'); 
    $item->addChild('title', htmlspecialchars($product['title'])); 
    $item->addChild('description', htmlspecialchars($product['title'])); 
    $item->addChild('link', $product['url']); 
    $item->addChild('id', $product['product_id'], "http://base.google.com/ns/1.0"); 
    $item->addChild('price', $product['price_latest'], "http://base.google.com/ns/1.0"); 
    $item->addChild('brand', $product['range'], "http://base.google.com/ns/1.0"); 
    $item->addChild('condition', 'new', "http://base.google.com/ns/1.0"); 
    $item->addChild('image_link', $product['image'], "http://base.google.com/ns/1.0"); 
} 

print_r($xml->asXML()); 

die Antwort ergeben würde:

<?xml version="1.0"?> 
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0"> 
    <channel> 
    <title>Removed</title> 
    <description>Removed</description> 
    <link>Removed</link> 
    <item> 
     <title>Foobar</title> 
     <description>Foobar</description><link/> 
     <g:id>00001</g:id> 
     <g:price>$3.50</g:price> 
     <g:brand>Foo</g:brand> 
     <g:condition>new</g:condition> 
     <g:image_link>https://f.oo.bar/image.tiff</g:image_link> 
    </item> 
    </channel> 
</rss>