Пятница, 19.04.2024. 15:46 | RSS
 Приветствуем Вас, Гость! Регистрация | Вход с параметрами
Меню сайта
Категории каталога
Обзоры оборудования [44]
Обзоры различного оборудования, побывавшего в руках автора сайта: ноутбуки, винчестеры, мониторы, материнские платы, видеокарты и др.
Тестирование [7]
Отчасти перекликается с обзорами оборудования, однако здесь сделан упор именно на тестирование в каких-либо бенчмарках
Практика апгрейда [9]
Статьи, посвященные вопросам апгрейда компьютера. Примеры конфигураций, оценка стоимости
Решение проблем [5]
Рассматриваются различные мелкие и не очень проблемы и их возможные варианты решения
Обзоры софта [6]
Обзоры программного обеспечения: антивирусы, браузеры, утилиты и др.
Игры [37]
Обзоры игр, советы по прохождению
Программирование, 1С [13]
Освещаются вопросы по программированию, в немалой степени - на платформе 1С: Предприятия
Цифровое фото, видео [5]
Выделенный раздел, посвященным цифровым фотоаппаратам и видеокамерам
Вход на сайт
Параллельные проекты
Друзья сайта
Статистика

Околокомпьютерный сайт Дмитрия Косолапова
Главная » Статьи » Программирование, 1С

Чтение XML в PHP

В статье рассматриваются основы работы с объектами типа XMLReader.
PHP - очень интересный язык программирования, предоставляющий массу возможностей, что называется, прямо из коробки. В последних версиях PHP (начиная с 5.1) в стандартную поставку вошел класс XMLReader, предназначенный для чтения файлов XML. В принципе, объект-то достаточно понятный, однако я столкнулся с одной неприятной особенностью, что и побудило меня написать данную статью. Но обо всем по порядку.

Итак, создаем объект класса XMLReader:

$reader = new XMLReader();

Далее нужно открыть файл. Для этого существуют два метода: Open и XML. Следуя букве описания языка, первый метод предназначен для открытия XML-файлов, а второй - для передачи XML-содержимого в виде строки. Однако довольно часто второй метод используется и для открытия файлов. Воспользуемся, например, Open:

if (!$reader->open('http://example.com/example.xml')) {
 die('Не удалось открыть файл');
}

В принципе, проверить результат открытия не повредит, хотя, сдается мне, пустой файл открывается с положительным результатом. Так что будьте внимательны. Еще один момент - поддерживается протокол http, поэтому вы можете разбирать файл, который может быть расположен вообще на другом сервере.

Теперь, собственно, переходим к разбору. Во-первых, если есть схема документа, то можно сразу же проверить его на соответствие схеме. Первый вариант - это когда в соответствии с полным стандартом XML схема указана в заголовке XML-файла. Тогда валидацию можно произвести так:

$reader->setParserProperty(XMLReader::VALIDATE, true);
if (!$reader->isValid()) {
 die('Неправильный XML');
}

Подобный код нужно вызывать после открытия документа (методом Open или XML), но до первого считывания (методом Read).

Другой вариант - это воспользоваться форматом описания схем RELAX NG, особенно если XML-документ достаточно простой. Тем более что такую схему можно описать в виде строки прямо в php-скрипте - необязательно иметь ее в виде файла. К сожалению, более-менее внятной спецификации Relax NG на русском языке я с ходу не нашел, пожалуй, можно обратиться к широко рекламируемым в последнее время пособиям от IBM, в частности к статье Валидация XML-документов. В этом случае для установки схемы Relax NG используются методы setRelaxNGSchema, если схема сохранена в виде файла, и setRelaxNGSchemaSource, если схема передается в виде строки.

Теперь самое время приступить к чтению. В общем-то, в самом общем виде это цикл while ($reader->read()). Если же быть чуть более конкретным, то у нас будет серия вложенных циклов, в которых мы будем смотреть на текущий так называемый тип узла (node) и его имя, и, в зависимости от этого, предпринимать какие-либо действия, например, открывать вложенный цикл. И так далее. Также мне представляется удобным сформировать из содержимого XML-файла ассоциативный массив, с которым потом и работать после считывания. Это, по идее, уменьшает вероятность ошибок и увеличивает гарантию того, что XML мы считали правильно.

Что же касается типов узлов, то их довольно много, наиболее важные из них:

  • XMLReader::ELEMENT - начало элемента (<element>)
  • XMLReader::ATTRIBUTE - атрибут элемента (<element hereisattribute="123">)
  • XMLReader::END_ELEMENT - конец элемента (</element>)
  • XMLReader::TEXT - содержимое элемента (в общем-то, это существенная часть между <element> и </element>, хотя в этой области также обитают, например XMLReader::WHITESPACE - незначащие пробелы - и т.п.)

Итак, набросок кода цикла по XML файлу:

$xmlarr = array();
$idx = 0;
while ($reader->read()) {
  if (($reader->nodeType == XMLReader::ELEMENT) && ($reader->name == 'Element1')) {
    // считываем атрибуты
    $xmlarr[$idx]['Attr1'] = $reader->getAttribute('Attr1');
 
    while ($reader->read()) {
      // разбираем вложенные элементы
      if (($reader->nodeType == XMLReader::ELEMENT) && ($reader->name == 'Element11')) {
        while ($reader->read()) {
          if ($reader->nodeType == XMLReader::TEXT) {
            // получаем значение из свойства $reader->value;
            $xmlarr[$idx][$reader->name] = $reader->value;
          }
          elseif (($reader->nodeType == XMLReader::ELEMENT) && ($reader->name == 'Element111')) {
            // еще один вложенный элемент
            while ($reader->read()) {
              if ($reader->nodeType == XMLReader::TEXT) /* и т.д. */ {
                $xmlarr[$idx]['Element11'][$reader->name] = $reader->value;
              }
              elseif (($reader->nodeType == XMLReader::END_ELEMENT) && ($reader->name == 'Element111')) {
                break;
              }
            }
          }
          elseif (($reader->nodeType == XMLReader::END_ELEMENT) && ($reader->name == 'Element11')) {
            break;
          }
        }
      }
      elseif (($reader->nodeType == XMLReader::ELEMENT) && ($reader->name == 'Element12')) {
        while ($reader->read()) {
          if ($reader->nodeType == XMLReader::TEXT) {
            // ... = $reader->value;
          }
          elseif (($reader->nodeType == XMLReader::END_ELEMENT) && ($reader->name == 'Element12')) {
            break;
          }
        }
      }
      elseif (($reader->nodeType == XMLReader::END_ELEMENT) && ($reader->name == 'Element1')) {
        $idx += 1;
        break;
      }
    }
  }
}

Итак, мы пользовались такими свойствами, как nodeType (тип узла), name (наименование узла) и value (значение), а также методом getAttribute для получения значения атрибута по заранее известному имени. Если же атрибуты заранее неизвестны, то тогда по аналогии открывается вложенный цикл с проверкой $reader->nodeType == XMLReader::ATTRIBUTE. В общем-то, здесь самое сложное не запутаться во вложенных циклах smile

А теперь, та самая неприятная особенность, с которой я столкнулся и из-за которой, в немалой степени, я и засел за написание данной статьи. Дело в том, что при использовании самозакрывающихся элементов (например, <element1 Attr1="error" />), в отличие, например, от парсера, который использует 1С: Предприятие 8 (видимо, какую-то Windows-библиотеку), не распознает /> как nodeType == XMLReader::END_ELEMENT. Так что мой совет - избегать подобного рода самозакрывающиеся элементы. Хотя я и не отрицаю возможности, что у меня были устаревшие библиотеки PHP (в частности libxml), поэтому в последних версиях, возможно, эта ошибка (я склонен все-таки считать эту особенность именно ошибкой) исправлена.

Ну что ж, надеюсь, данной статьей я, так сказать, ввел вас в курс дела, ну а для более серьезного изучения вопроса я, пожалуй, снова сошлюсь на IBM-овское пособие - Синтаксический анализ XML в PHP, однако нужно иметь в виду, что та статья довольно-таки обширная, и там рассматривается данный вопрос с точки зрения организации AJAX.

P.S. Не забудьте закрыть XML-файл wink

$reader->close();
Категория: Программирование, 1С | Добавил: Vetkhy (03.05.2009) | Автор: Дмитрий Косолапов | Просмотров: 14820

Метки: PHP, чтение, XMLReader, XML

Всего комментариев: 3
0  
1 Mrb   (31.10.2009 22:02) [Материал]
Решение для этой проблемы следующее:

if (($reader->nodeType == XMLReader::ELEMENT) && ($reader->name == 'Element11') && $reader->isEmptyElement == false) { //код... }

В этом случае скрипт будет проверять не является ли элемент пустым, то бишь самозакрывающимся.


0  
2 Vetkhy   (01.11.2009 09:32) [Материал]
Спасибо за подсказку!

0  
3 Виталий   (12.12.2012 11:14) [Материал]
$reader->open('http://example.com/example.xml', NULL, LIBXML_NOEMPTYTAG);

Вот и всё решение проблемы.
LIBXML_NOEMPTYTAG (integer) - Разворачивать пустые тэги (например <br/> в <br></br>

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Сайт управляется системой uCozДмитрий Косолапов © 2007-2024