2010-08-04 5 views
7

Tôi đã dành cả ngày với chức năng DOM của PHP nhưng tôi không thể hiểu được cách hoạt động của nó. :( Tôi có một tập tin XML đơn giản mà trông ổn nhưng tôi không thể sử dụng nó như thế nào tôi nghĩ rằng khi tôi đã tạo ra nó là cấu trúcĐơn giản hóa việc phân tích cú pháp XML DOM PHP - làm cách nào?

Sample đoạn XML:.

-pages //root element 
    -page id="1" //we can have any number of pages 
     -product id="364826" //we can have any number of products 
      -SOME_KIND_OF_VALUE 
      -ANOTHER_VALUE 
      ... 

ý tưởng ban đầu của tôi là để tăng tốc độ quy trình làm việc khách hàng của tôi vì vậy tôi ném ra CSV cũ và bắt đầu sử dụng XMLs

vấn đề 1:.. Khi tôi nhóm sản phẩm vào trang tôi đang sử dụng setIdAttribute để ngăn chặn việc lưu trữ cùng một trang trong cây nhiều hơn một lần tác phẩm này tốt cho đến khi đọc xảy ra bởi vì các id này được gắn với một số loại DTD (dựa trên getElementById).

Câu hỏi 1: Tôi có thể viết một DTD đơn giản cung cấp những thông tin cần thiết để tôi có thể sử dụng getElementById ở giai đoạn đọc?

Bài toán 2: Vì tôi có các trang tôi muốn tải ít thông tin nhất có thể. Đó là lý do tại sao tôi tạo thuộc tính id trên các trang. Bây giờ tôi không thể truy cập trang của tôi id = "2" trực tiếp vì vấn đề 1 ở trên (getElementById không có ý nghĩa). Bằng cách nào đó tôi có thể quản lý để lấy các thông tin cần thiết về từng sản phẩm trên một trang nhất định nhưng mã của tôi trông đáng sợ:

$doc  = DOMDocument::load('data.xml'); 
$xpath = new DOMXPath($doc); 
$query = '/pages/page[' . $page . ']'; //$page is fine: was set earlier 
$products = $xpath->query($query); 
$_prods = $doc->getElementsByTagName('product'); 
foreach($_prods as $product){ 
    foreach($product->childNodes as $node){ 
     echo $node->nodeName . ": " . $node->nodeValue . "<br />"; 
    } 
} 

Queston 2: Tôi nghĩ rằng đoạn code trên là ví dụ về cách không để phân tích một XML. Nhưng vì kiến ​​thức hạn chế của tôi về các hàm DOM của PHP, tôi không thể tự viết một cái sạch hơn. Tôi đã thử một số giải pháp tầm thường nhưng không ai trong số họ làm việc cho tôi.

Hãy giúp tôi nếu có thể.

Cảm ơn, Fabrik

+0

Tôi cho rằng bạn vừa đưa ra một phác thảo về cấu trúc của tài liệu XML của bạn? Bởi vì nó không phải là XML những gì bạn đã đăng (tôi chỉ muốn chắc chắn;)). –

+0

Tất nhiên nó chỉ là một phác thảo. XML xác nhận tốt và có vẻ không giống mã của tôi: o – fabrik

+0

các hàm simplexml có quá đơn giản cho các nhu cầu của bạn không? – stillstanding

Trả lời

12

Giải quyết Vấn đề 1:

Các W3C defines: ý nghĩa của các thuộc tính xml:id như một thuộc tính ID trong tài liệu XML và xác định xử lý của thuộc tính này để xác định ID trong không có xác nhận hợp lệ, mà không tìm nạp tài nguyên bên ngoài và không phụ thuộc vào một tập hợp con bên trong.

Nói cách khác, khi bạn sử dụng

$element->setAttribute('xml:id', 'test'); 

bạn không cần phải gọi setIdAttribute, cũng không phải chỉ định một DTD hay Schema. DOM sẽ nhận ra thuộc tính xml:id khi được sử dụng với getElementById mà không cần phải xác thực tài liệu hoặc bất kỳ thứ gì. Đây là cách tiếp cận ít nỗ lực nhất. Tuy nhiên, lưu ý rằng tùy thuộc vào hệ điều hành và phiên bản libxml của bạn, bạn sẽ không nhận được getElementById để hoạt động.

Giải quyết Problem2:

Ngay cả với ID không được fetchable với getElementById, bạn có thể vẫn còn rất nhiều lấy chúng với XPath:

$xpath->query('/pages/page[@id=1]'); 

chắc chắn sẽ làm việc. Và bạn cũng có thể lấy trẻ em sản phẩm cho một trang cụ thể trực tiếp:

$xpath->query('//pages/page[@id=1]/products'); 

Ngoài ra, có rất ít bạn có thể làm để làm cho DOM đang nhìn ít tiết, bởi vì nó thực sự là một giao diện verbose. Nó phải là, bởi vì DOM is a language agnostic interface, again defined by the W3C.


EDIT sau khi bình luận dưới đây

Nó đang làm việc như tôi đã giải thích ở trên. Đây là một trường hợp thử nghiệm đầy đủ cho bạn. Phần đầu tiên là dành cho viết tệp XML mới với DOM. Đó là nơi bạn cần đặt thuộc tính xml:id. Bạn sử dụng tùy chọn này thay vì thuộc tính id, không phải không gian tên thông thường.

// Setup 
$dom = new DOMDocument; 
$dom->formatOutput = TRUE; 
$dom->preserveWhiteSpace = FALSE; 
$dom->loadXML('<pages/>'); 

// How to set a valid id attribute when not using a DTD or Schema 
$page1 = $dom->createElement('page'); 
$page1->setAttribute('xml:id', 'p1'); 
$page1->appendChild($dom->createElement('product', 'foo1')); 
$page1->appendChild($dom->createElement('product', 'foo2')); 

// How to set an ID attribute that requires a DTD or Schema when reloaded 
$page2 = $dom->createElement('page'); 
$page2->setAttribute('id', 'p2'); 
$page2->setIdAttribute('id', TRUE); 
$page2->appendChild($dom->createElement('product', 'bar1')); 
$page2->appendChild($dom->createElement('product', 'bar2')); 

// Appending pages and saving XML 
$dom->documentElement->appendChild($page1); 
$dom->documentElement->appendChild($page2); 
$xml = $dom->saveXML(); 
unset($dom, $page1, $page2); 
echo $xml; 

này sẽ tạo ra một file XML như thế này:

<?xml version="1.0"?> 
<pages> 
    <page xml:id="p1"> 
    <product>foo1</product> 
    <product>foo2</product> 
    </page> 
    <page id="p2"> 
    <product>bar1</product> 
    <product>bar2</product> 
    </page> 
</pages> 

Khi bạn đọc trong XML một lần nữa, ví dụ DOM mới không còn biết bạn đã tuyên bố không namespaced id thuộc tính như Thuộc tính ID với setIdAttribute. Nó sẽ vẫn nằm trong XML, nhưng thuộc tính id sẽ chỉ là một thuộc tính thông thường. You have to be aware that ID attributes are special in XML.

// Load the XML we created above 
$dom = new DOMDocument; 
$dom->loadXML($xml); 

Bây giờ cho một số xét nghiệm:

echo "\n\n GETELEMENTBYID RETURNS ELEMENT WITH XML:ID \n\n"; 
foreach($dom->getElementById('p1')->childNodes as $product) { 
    echo $product->nodeValue; // Will output foo1 and foo2 with whitespace 
} 

Các công trình trên, bởi vì một DOM phân tích cú pháp phù hợp có nhận xml:id là một thuộc tính ID, không phân biệt bất kỳ DTD hay Schema. Điều này được giải thích trong các thông số kỹ thuật được liên kết ở trên. Lý do kết quả đầu ra khoảng trắng là do đầu ra được định dạng có các nút DOMText giữa thẻ mở, hai thẻ sản phẩm và thẻ đóng, vì vậy chúng tôi đang lặp qua năm nút. Khái niệm nút là rất quan trọng để hiểu khi làm việc với XML.

echo "\n\n GETELEMENTBYID CANNOT FETCH NORMAL ID \n\n"; 
foreach($dom->getElementById('p2')->childNodes as $product) { 
    echo $product->nodeValue; // Will output a NOTICE and a WARNING 
} 

Ở trên sẽ không hoạt động, vì id không phải là thuộc tính ID. Để trình phân tích cú pháp DOM nhận ra nó như vậy, bạn cần một DTD hoặc Schema và XML phải được xác nhận hợp lệ với nó.

echo "\n\n XPATH CAN FETCH NORMAL ID \n\n"; 
$xPath = new DOMXPath($dom); 
$page2 = $xPath->query('/pages/page[@id="p2"]')->item(0); 
foreach($page2->childNodes as $product) { 
    echo $product->nodeValue; // Will output bar1 and bar2 
} 

XPath mặt khác là theo nghĩa đen về các thuộc tính, có nghĩa là bạn có thể truy vấn DOM cho các phần tử trang với thuộc tính id nếu getElementById không có sẵn. Lưu ý rằng để truy vấn trang có ID p1, bạn phải bao gồm không gian tên, ví dụ: @xml:id="p1".

echo "\n\n XPATH CAN FETCH PRODUCTS FOR PAGE WITH ID \n\n"; 
$xPath = new DOMXPath($dom); 
foreach($xPath->query('/pages/page[@id="p2"]/product') as $product) { 
    echo $product->nodeValue; // Will output bar1 and bar2 w\out whitespace 
} 

Và như đã nói, bạn cũng có thể sử dụng XPath để truy vấn bất kỳ điều gì khác trong tài liệu.Điều này sẽ không xuất ra khoảng trắng, vì nó sẽ chỉ trả về các phần tử product bên dưới trang có id p2.

Bạn cũng có thể duyệt toàn bộ DOM từ nút. Đó là một cấu trúc cây. Vì DOMNode là lớp quan trọng nhất trong DOM, bạn muốn làm quen với nó.

echo "\n\n TRAVERSING UP AND DOWN \n\n"; 
$product = $dom->getElementsByTagName('product')->item(2); 
echo $product->tagName; // 'product' 
echo $dom->saveXML($product); // '<product>bar1</product>' 

// Going from bar1 to foo1 
$product = $product->parentNode // Page Node 
        ->parentNode // Pages Node 
        ->childNodes->item(1) // Page p1 
        ->childNodes->item(1); // 1st Product 

echo $product->nodeValue; // 'foo1' 

// from foo1 to foo2 it is two(!) nodes because the XML is formatted 
echo $product->nextSibling->nodeName; // '#text' with whitespace and linebreak 
echo $product->nextSibling->nextSibling->nodeName; // 'product' 
echo $product->nextSibling->nextSibling->nodeValue; // 'foo2' 

Trên sidenote, vâng, tôi có lỗi đánh máy trong mã gốc ở trên. Đó là product không phải products. Nhưng tôi thấy khó có thể khẳng định mã không hoạt động khi tất cả những gì bạn phải thay đổi là s. Điều đó chỉ cảm thấy quá nhiều như muốn được spoonfed.

+0

Đặt 'id' của một trang trước khi ghi tệp XML đang hoạt động tốt. Khi tôi đọc XML tôi không thể/không muốn thiết lập thuộc tính vì tôi muốn đọc nguồn XML dựa trên các thuộc tính này. Vì vậy, vấn đề 1 chưa được giải quyết. Vấn đề 2 chắc chắn không được giải quyết, truy vấn XPath đầu tiên của bạn không thành công. Thứ hai không thành công vì tôi không có nút 'products' thay vào đó tôi có nhiều nút' product' bên trong một trang. (Điều đó đã được xác định trong câu hỏi của tôi.) – fabrik

+0

@fabrik cả hai vấn đề đều được giải quyết. Xem bản cập nhật của tôi để tìm bằng chứng. – Gordon

+1

Thật tuyệt vời! Cảm ơn bạn đã giải thích sâu sắc của bạn. Nó nhanh và thực hiện chính xác những gì tôi muốn. Ngoại trừ một điều nhưng đó là lỗi của tôi: Tôi đã phạm sai lầm trong đoạn XML mẫu vì tôi cần tên và giá trị của nút quá vì vậy tôi cần hai foreach một lần nữa: o Tất nhiên tôi sẽ chấp nhận câu trả lời của bạn bởi vì nó làm thủ thuật . Cảm ơn một lần nữa! – fabrik