У меня есть XML-файл, который очень велик (миллионы записей). Из-за ограничений скорости и памяти я планирую использовать XMLReader
/ XMLWriter
.
Мне нужно прочитать файл, получить одну запись, изменить ее атрибут и, наконец, сохранить XML снова.
Для тестирования я создал XML-файл и записал в него несколько записей, используя следующие строки:
$doc = new XMLWriter(); $doc->openURI($xmlFile); $doc->startDocument('1.0','UTF-8'); $doc->setIndent(4); $doc->startElement('DBOS'); for($r=0;$r<10; $r++){ $doc->startElement('ITEMS'); for($i=0;$i<5; $i++){ $doc->startElement('ITEM'); $doc->writeAttribute('id', $r.'-'.$i); $doc->endElement(); } $doc->endElement(); } $doc->endElement(); $doc->endDocument(); $doc->flush();
Я прочитал его снова, используя это:
$reader = new XMLReader(); if (!$reader->open($xmlFile)){ die("Failed to open 'data.xml'"); } while($reader->read()){ if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'ITEMS') { $node = $reader->expand(); $items = $node->childNodes; foreach ($items as $ik => $itm ){ print $itm->textContent.'<br/>'; // how to change the ID Attribute of a Node (DomNode) and save changes to the original XML File } break; } } $reader->close();
в$reader = new XMLReader(); if (!$reader->open($xmlFile)){ die("Failed to open 'data.xml'"); } while($reader->read()){ if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'ITEMS') { $node = $reader->expand(); $items = $node->childNodes; foreach ($items as $ik => $itm ){ print $itm->textContent.'<br/>'; // how to change the ID Attribute of a Node (DomNode) and save changes to the original XML File } break; } } $reader->close();
Мой вопрос : как изменить атрибут id
в DomNode
и сохранить изменения в исходном файле XML с помощью XMLWriter ?
Как изменить атрибут id DomNode и сохранить изменения в исходном файле XML с помощью XMLWriter еще раз?
Это не работает. Если вы используете XMLReader и XMLWriter для работы с одним и тем же файлом одновременно, файл будет усечен писателем, и читатель выплюнет ошибки и перестанет работать.
Однако вы можете работать с разными файлами.
Итак, что вы можете сделать, это использовать XMLReader для чтения документа, а во время работы с ним используйте XMLWriter для записи в другой документ на основе того, что вы читали и время от времени изменяли. После того, как вы закончите, вы можете переименовать вновь написанный файл в старое имя файла.
Для XML-документа (сокращенного для примера, XMLReader и XMLWriter имеют естественный смысл с действительно огромными документами), как этот, который немного поменялся после вашего вопроса:
<DBOS> <ITEMS> <ITEM>item #1</ITEM> <ITEM>item #2</ITEM> <ITEM>item #3</ITEM> </ITEMS> <ITEMS> <ITEM>item #4</ITEM> <ITEM>item #5</ITEM> </ITEMS> </DBOS>
Пример рабочего кода:
<?php /* * This file is part of the XMLReaderIterator package. * * Copyright (C) 2012, 2014 hakre <http://hakre.wordpress.com> * * Example: Write XML with XMLWriter while reading from XMLReader with XMLWriterIteration */ require('xmlreader-iterators.php'); // require XMLReaderIterator library $xmlInputFile = 'data/dobs-items.xml'; $xmlOutputFile = 'php://output'; $reader = new XMLReader(); $reader->open($xmlInputFile); $writer = new XMLWriter(); $writer->openUri($xmlOutputFile); $iterator = new XMLWritingIteration($writer, $reader); $writer->startDocument(); $itemsCount = 0; $itemCount = 0; foreach ($iterator as $node) { $isElement = $node->nodeType === XMLReader::ELEMENT; if ($isElement && $node->name === 'ITEMS') { // increase counter for <ITEMS> elements and reset <ITEM> counter $itemsCount++; $itemCount = 0; } if ($isElement && $node->name === 'ITEM') { // increase <ITEM> counter and insert "id" attribute $itemCount++; $writer->startElement($node->name); $writer->writeAttribute('id', $itemsCount . "-" . $itemCount); if ($node->isEmptyElement) { $writer->endElement(); } } else { // handle everything else $iterator->write(); } } $writer->endDocument();
-<?php /* * This file is part of the XMLReaderIterator package. * * Copyright (C) 2012, 2014 hakre <http://hakre.wordpress.com> * * Example: Write XML with XMLWriter while reading from XMLReader with XMLWriterIteration */ require('xmlreader-iterators.php'); // require XMLReaderIterator library $xmlInputFile = 'data/dobs-items.xml'; $xmlOutputFile = 'php://output'; $reader = new XMLReader(); $reader->open($xmlInputFile); $writer = new XMLWriter(); $writer->openUri($xmlOutputFile); $iterator = new XMLWritingIteration($writer, $reader); $writer->startDocument(); $itemsCount = 0; $itemCount = 0; foreach ($iterator as $node) { $isElement = $node->nodeType === XMLReader::ELEMENT; if ($isElement && $node->name === 'ITEMS') { // increase counter for <ITEMS> elements and reset <ITEM> counter $itemsCount++; $itemCount = 0; } if ($isElement && $node->name === 'ITEM') { // increase <ITEM> counter and insert "id" attribute $itemCount++; $writer->startElement($node->name); $writer->writeAttribute('id', $itemsCount . "-" . $itemCount); if ($node->isEmptyElement) { $writer->endElement(); } } else { // handle everything else $iterator->write(); } } $writer->endDocument();
-<?php /* * This file is part of the XMLReaderIterator package. * * Copyright (C) 2012, 2014 hakre <http://hakre.wordpress.com> * * Example: Write XML with XMLWriter while reading from XMLReader with XMLWriterIteration */ require('xmlreader-iterators.php'); // require XMLReaderIterator library $xmlInputFile = 'data/dobs-items.xml'; $xmlOutputFile = 'php://output'; $reader = new XMLReader(); $reader->open($xmlInputFile); $writer = new XMLWriter(); $writer->openUri($xmlOutputFile); $iterator = new XMLWritingIteration($writer, $reader); $writer->startDocument(); $itemsCount = 0; $itemCount = 0; foreach ($iterator as $node) { $isElement = $node->nodeType === XMLReader::ELEMENT; if ($isElement && $node->name === 'ITEMS') { // increase counter for <ITEMS> elements and reset <ITEM> counter $itemsCount++; $itemCount = 0; } if ($isElement && $node->name === 'ITEM') { // increase <ITEM> counter and insert "id" attribute $itemCount++; $writer->startElement($node->name); $writer->writeAttribute('id', $itemsCount . "-" . $itemCount); if ($node->isEmptyElement) { $writer->endElement(); } } else { // handle everything else $iterator->write(); } } $writer->endDocument();
-<?php /* * This file is part of the XMLReaderIterator package. * * Copyright (C) 2012, 2014 hakre <http://hakre.wordpress.com> * * Example: Write XML with XMLWriter while reading from XMLReader with XMLWriterIteration */ require('xmlreader-iterators.php'); // require XMLReaderIterator library $xmlInputFile = 'data/dobs-items.xml'; $xmlOutputFile = 'php://output'; $reader = new XMLReader(); $reader->open($xmlInputFile); $writer = new XMLWriter(); $writer->openUri($xmlOutputFile); $iterator = new XMLWritingIteration($writer, $reader); $writer->startDocument(); $itemsCount = 0; $itemCount = 0; foreach ($iterator as $node) { $isElement = $node->nodeType === XMLReader::ELEMENT; if ($isElement && $node->name === 'ITEMS') { // increase counter for <ITEMS> elements and reset <ITEM> counter $itemsCount++; $itemCount = 0; } if ($isElement && $node->name === 'ITEM') { // increase <ITEM> counter and insert "id" attribute $itemCount++; $writer->startElement($node->name); $writer->writeAttribute('id', $itemsCount . "-" . $itemCount); if ($node->isEmptyElement) { $writer->endElement(); } } else { // handle everything else $iterator->write(); } } $writer->endDocument();
-<?php /* * This file is part of the XMLReaderIterator package. * * Copyright (C) 2012, 2014 hakre <http://hakre.wordpress.com> * * Example: Write XML with XMLWriter while reading from XMLReader with XMLWriterIteration */ require('xmlreader-iterators.php'); // require XMLReaderIterator library $xmlInputFile = 'data/dobs-items.xml'; $xmlOutputFile = 'php://output'; $reader = new XMLReader(); $reader->open($xmlInputFile); $writer = new XMLWriter(); $writer->openUri($xmlOutputFile); $iterator = new XMLWritingIteration($writer, $reader); $writer->startDocument(); $itemsCount = 0; $itemCount = 0; foreach ($iterator as $node) { $isElement = $node->nodeType === XMLReader::ELEMENT; if ($isElement && $node->name === 'ITEMS') { // increase counter for <ITEMS> elements and reset <ITEM> counter $itemsCount++; $itemCount = 0; } if ($isElement && $node->name === 'ITEM') { // increase <ITEM> counter and insert "id" attribute $itemCount++; $writer->startElement($node->name); $writer->writeAttribute('id', $itemsCount . "-" . $itemCount); if ($node->isEmptyElement) { $writer->endElement(); } } else { // handle everything else $iterator->write(); } } $writer->endDocument();
Затем вывод будет (пример для стандартного вывода, может использоваться любое допустимое имя файла PHP):
<?xml version="1.0"?> <DBOS> <ITEMS> <ITEM id="1-1">item #1</ITEM> <ITEM id="1-2">item #2</ITEM> <ITEM id="1-3">item #3</ITEM> </ITEMS> <ITEMS> <ITEM id="2-1">item #4</ITEM> <ITEM id="2-2">item #5</ITEM> </ITEMS> </DBOS>
Как показано в этом примере, атрибуты id добавляются на основе нумерации различными переменными счетчика.
XMLWritingIteration делает это легко, поскольку он имеет дело со всеми другими узлами и случаями благодаря $iterator->write()
.
Пример и код являются частью пакета XMLReaderIterator . Существует еще один пример создания DOMDocument на основе XMLReader , он является частью ответа на вопрос «Как отличить пустую строку от нулевого размера в DOMDocument?» ,