Как читать XML-файл с неопределенным пространством имен с помощью XMLReader?

Я относительно новичок в анализе XML-файлов и пытаюсь прочитать большой XML-файл с XMLReader.

<?xml version="1.0" encoding="UTF-8"?> <ShowVehicleRemarketing environment="Production" lang="en-CA" release="8.1-Lite" xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd"> <ApplicationArea> <Sender> <Component>Component</Component> <Task>Task</Task> <ReferenceId>w5/cron</ReferenceId> <CreatorNameCode>CreatorNameCode</CreatorNameCode> <SenderNameCode>SenderNameCode</SenderNameCode> <SenderURI>http://www.example.com</SenderURI> <Language>en-CA</Language> <ServiceId>ServiceId</ServiceId> </Sender> <CreationDateTime>CreationDateTime</CreationDateTime> <Destination> <DestinationNameCode>example</DestinationNameCode> </Destination> </ApplicationArea> ... 

Я получаю следующую ошибку

ErrorException [Warning]: XMLReader :: read () [xmlreader.read]: compress.zlib: // D: /WebDev/example/local/public/../upload/example.xml.gz: 2: ошибка пространства имен: Префикс пространства имен xsi для schemaLocation на ShowVehicleRemarketing не определен

Я искал вокруг и не могу найти много полезной информации об использовании XMLReader для чтения XML-файлов с пространствами имен. Как бы я хотел определить пространство имен, если это на самом деле то, что мне нужно сделать .. мало помогает? ссылки на соответствующие ресурсы?

Должно быть определение пространства имен xsi . Например

 <ShowVehicleRemarketing environment="Production" lang="en-CA" release="8.1-Lite" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.starstandards.org/STAR/STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd" > 

Обновление: вы можете написать определенный пользователем фильтр, а затем позволить XMLReader использовать этот фильтр , например:

 stream_filter_register('darn', 'DarnFilter'); $src = 'php://filter/read=darn/resource=compress.zlib://something.xml.gz'; $reader->open($src); 

Содержимое, считанное оболочкой compress.zlib, затем «маршрутизируется» через DarnFilter, который должен найти (первое) место, где он может вставить объявление xmlns: xsi. Но это довольно беспорядочно и потребует некоторой возможности сделать это правильно (например, теоретически ведро A может содержать xs , bucket B i:schem и ведро C aLocation=" )


Обновление 2: вот специальный пример фильтра в php, который вставляет объявление пространства имен xsi. В основном непроверенный (работал с одним тестом, который я запускал ;-)) и недокументировал. Возьмите его как доказательство концепции, а не производственный код.

 <?php stream_filter_register('darn', 'DarnFilter'); $src = 'php://filter/read=darn/resource=compress.zlib://d:/test.xml.gz'; $r = new XMLReader; $r->open($src); while($r->read()) { echo '.'; } class DarnFilter extends php_user_filter { protected $buffer=''; protected $status = PSFS_FEED_ME; public function filter($in, $out, &$consumed, $closing) { while ( $bucket = stream_bucket_make_writeable($in) ) { $consumed += $bucket->datalen; if ( PSFS_PASS_ON == $this->status ) { // we're already done, just copy the content stream_bucket_append($out, $bucket); } else { $this->buffer .= $bucket->data; if ( $this->foo() ) { // first element found // send the current buffer $bucket->data = $this->buffer; $bucket->datalen = strlen($bucket->data); stream_bucket_append($out, $bucket); $this->buffer = null; // no need for further processing $this->status = PSFS_PASS_ON; } } } return $this->status; } /* looks for the first (root) element in $this->buffer * if it doesn't contain a xsi namespace decl inserts it */ protected function foo() { $rc = false; if ( preg_match('!<([^?>\s]+)\s?([^>]*)>!', $this->buffer, $m, PREG_OFFSET_CAPTURE) ) { $rc = true; if ( false===strpos($m[2][0], 'xmlns:xsi') ) { echo ' inserting xsi decl '; $in = '<'.$m[1][0] . ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' . $m[2][0] . '>'; $this->buffer = substr($this->buffer, 0, $m[0][1]) . $in . substr($this->buffer, $m[0][1] + strlen($m[0][0])); } } return $rc; } } 

Обновление 3: И вот ad-hoc решение, написанное на C #

 XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); // prime the XMLReader with the xsi namespace nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); using ( XmlReader reader = XmlTextReader.Create( new GZipStream(new FileStream(@"\test.xml.gz", FileMode.Open, FileAccess.Read), CompressionMode.Decompress), new XmlReaderSettings(), new XmlParserContext(null, nsmgr, null, XmlSpace.None) )) { while (reader.Read()) { System.Console.Write('.'); } } 

Вы можете file_get_contents и str_replace XML, прежде чем передавать его в XMLReader .

Либо вставьте требуемую декларацию пространства имен для префикса xsi:

 $reader = new XMLReader; $reader->xml(str_replace( '<ShowVehicleRemarketing', '<ShowVehicleRemarketing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"', file_get_contents('http://example.com/data.xml'))); 

Другой вариант – удалить schemaLocation :

 $reader->xml(str_replace( 'xsi:schemaLocation="http://www.starstandards.org/STAR /STAR/Rev4.2.4/BODs/Standalone/ShowVehicleRemarketing.xsd"', '', file_get_contents('http://example.com/data.xml'))); 

Однако, если в документе больше префиксов, вам придется заменить их все.

Пространство имен xsi обычно зарезервировано для использования с пространством имен экземпляров Schema :

 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 

если это не так, ваш XML-файл не соответствует требованиям XML + NS и не может быть проанализирован. Поэтому вы должны решить это в исходном документе.

Заметка о xsi: она даже более важна, чем некоторые другие пространства имен, поскольку она направляет проверяющий синтаксический анализатор на правильные расположения схем для схемы вашего XML.

Либо исправить все, что выписывает неверный XML, либо написать отдельный инструмент для выполнения исправления позже. (Он не должен читать все это в памяти одновременно, обязательно – поток данных в / из, возможно, чтение и запись строки за раз).

Таким образом, ваш код чтения не должен беспокоиться о попытке сделать что-то полезное с данными и исправить его одновременно.