2012-12-20 33 views
5

Tôi đang viết nguồn cấp dữ liệu RSS của sản phẩm Google bằng SimpleXML trong PHP. Tôi đã có các sản phẩm của tôi đến từ cơ sở dữ liệu và tạo ra các tập tin RSS tốt, nhưng có vấn đề khi nói đến không gian tên.Cách sử dụng không gian tên khi viết tệp XML bằng SimpleXML

Tôi đã tìm kiếm và tìm kiếm Stack Overflow và xem qua hàng tá bài đăng về cách phân tích cú pháp nguồn cấp dữ liệu XML, nhưng vấn đề của tôi thực sự là authoring một tệp XML có không gian tên.

Dưới đây là những gì các tập tin nên trông giống như:

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

Và đây là mã của tôi:

<?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']); 
} 

Làm thế nào để giới thiệu không gian tên g, cả hai tuyên bố xmlns trong thư mục gốc rss phần tử và sau đó làm tiền tố cho id, price, brand, conditionimage_link trong mỗi thành phần item?

+0

tôi khuyên bạn nên sử dụng [DOM] (http://php.net/manual/en/book .dom.php) để thay thế. SimpleXML chỉ là - * đơn giản *. Những gì bạn đang làm bây giờ đã rời khỏi cõi đơn giản. Bạn cần một API đầy đủ tính năng cho việc này. – DaveRandom

+0

Bạn đã có một ví dụ về cách tôi sẽ thêm một không gian tên với DOM? –

+0

@MartinBean Có một ví dụ trong [manual cho createElementNS của DOMDocument] (http://php.net/manual/domdocument.createelementns.php). – Furgas

Trả lời

6

Dưới đây là một ví dụ về cách để làm điều này bằng 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

Cảm ơn, Dave. Hoàn hảo! Chỉ cần quản lý để viết một giải pháp làm việc bản thân mình rất giống với giải pháp của bạn: https://gist.github.com/4344973 –

+0

@MartinBean Cool :-) Chỉ có sự khác biệt thực sự giữa bạn và của tôi là tôi sử dụng tạo nút văn bản rõ ràng cho động các giá trị, là (IMHO) là cách tốt nhất để đối phó với các thực thể thoát, bởi vì bạn có thể truyền dữ liệu thô vào và nó sẽ được tự động thoát mà không làm rối tung xung quanh với 'htmlspecialchars()'. Tuy nhiên tôi thừa nhận nó làm cho nó * thậm chí nhiều hơn * tiết - DOM không phải là một API ngắn gọn, nhưng nó là một trong những linh hoạt nhất có sẵn. – DaveRandom

+0

Điều cần biết, vì tôi thực sự có tiêu đề sản phẩm, vv được bao bọc trong 'htmlspecialchars()' trong mã thực tế! Tôi sẽ thay thế bằng phương pháp tạo rõ ràng một nút văn bản, như bạn đã làm. –

9

này thể được thực hiện bằng cách sử dụng giao diện SimpleXMLElement. IMHO đây là một hack của thứ tự dodgiest, nhưng nó hoạt động ngay bây giờ.

Điểm mấu chốt là nó hoạt động như sau, hiện tại, nhưng không thể tiếp tục hoạt động. Như vậy tôi không có cách nào đề xuất điều này qua câu trả lời được chấp nhận bởi @DaveRandom. Thay vào đó, tôi sẽ đưa câu trả lời này vào đây để những người khác trong tương lai có thể đọc được điều này và tiết kiệm thời gian để tìm cách làm SimpleXMLElement, và chỉ cần làm theo cách tiếp cận dựa trên DOM của DaveRandom :-)

Bạn có thể "lừa "trình phân tích cú pháp ngăn không cho tiền tố g: không gian tên của bạn từ tên phần tử của bạn, bằng cách đặt tiền tố" rác "trước tiền tố thực của bạn, ví dụ: "blah:g:condition".

Tôi đã thấy các biến thể của câu trả lời này để sử dụng trong các tiền tố thuộc tính, nhưng không nhìn thấy các tiền tố phần tử. Và tất cả những điều đó dường như đề xuất sử dụng "xmlns:yourPrefix:yourAttribute" và chuyển vào không gian tên đầy đủ như tham số thứ ba, trong thực tế (ít nhất, từ thử nghiệm cá nhân của riêng tôi) phần xmlns: có thể là khá nhiều thứ (bao gồm khoảng trắng!) Trước colon :, nhưng phải có một cái gì đó trước dấu hai chấm đầu tiên đó, tức là ":g:condition" sẽ không hoạt động. Và trừ khi bạn thực sự tạo ra một nút khai báo không gian tên và tiền tố, XML được trả về sẽ không hợp lệ (ví dụ tiền tố không gian tên bạn tấn công vào các phần tử của bạn sẽ không có khai báo).

Vì vậy, dựa trên mã ban đầu của bạn, bạn sẽ làm như sau. Cũng lưu ý việc bổ sung rõ ràng của không gian tên trong khai báo nút gốc (mặc dù điều này có thể được thực hiện thông qua API - nhưng tại sao lại bận tâm?).

$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

Bạn đã tiết kiệm thời gian của tôi. Cảm ơn Man :) – nit3ch

+0

Cảm ơn bạn đã hứa! –

1

Điều này có thể được thực hiện bằng cách thêm khai báo không gian tên vào tạo phần tử gốc cùng với thêm không gian tên vào hàm addChild.

Việc sửa đổi để mã gốc:

$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()); 

sẽ mang lại phản ứng:

<?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> 
Các vấn đề liên quan