In-Portal Developers Guide

This is a wiki-based Developers Guide for In-Portal Open Source CMS. The purpose of this guide is to provide advanced users, web developers and programmers with documentation on how to expand, customize and improve the functionality and the code the In-Portal software. Please consider contributing to our documentation writing effort.

K4:Обработка XML документов

From In-Portal Developers Guide

Jump to: navigation, search
Вспомогательные классы Вспомогательные классы
Статьи в этой категории
  • Обработка XML документов

XML is a convenient tree-structure format of organizing data. Each element can contain a value and/or other elements. Text inside an element can alternate with child elements, but it will still be considered a single value. Moreover, each element can have attributes. All of this is shown in the below example (let it be a file called "sample.xml"):

<?xml version="1.0" encoding="utf-8"?>
<our_document>
	<some_tag>
		Some tag's content.
	</some_tag>
	<planet_earth>
	    <continent id="1">
		North America
            	<country id="1">
			Canada
		</country>
          	<country id="2">
			USA
		</country>
	    </continent>
	    <continent id="2">
	    	Europe
            	<country id="1">
			Estonia
		</country>
            	<country id="2">
			Latvia
		</country>
            	<country id="3">
			Lithuania
		</country>
	    </continent>
	</planet_earth>
</our_document>

Contents

The CDATA Block

If the content of an element needs to contain symbols that could disrupt the correct parsing of an XML document, then the contents need to be inside a CDATA block. Some of these symbols include ">", "<" и "&". The syntax of a CDATA block looks like this:

<![CDATA[Some symbolic data, that > breaks & xml]]>

Data inside a CDATA block should not contain "]]>" because it will considered the end of the block. If it's absolutely necessary to have "]]>" then the following approach will work:

<![CDATA[]]]]><![CDATA[>]]>

In the above example, the contents are separated into two parts, each of which is inside a CDATA block. It's especially convenient to use CDATA in the situation where XML needs to be written to show to a user, but won't be processed when parsing the XML document.

Getting an XML Document

Before parsing an XML document, it has to be received.

Getting a Local File

To get a local file (i.e. located on the same computer as the site), the standard file_get_contents function can be used. This function returns the contents of file, the path of which is passed as its first argument.

$file_contents = file_get_contents(WRITEABLE . '/user_files/sample.xml');

Getting a Remote File

The file_get_contents function also allows retrieving the contents of a remote file. However, for security reasons, the server may restrict this functionality. Therefore, it's strongly recommended to use the standard K4 kCurlHelper class to retrieve remote files.

$curl_helper =& $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
 
$xml_data = $curl_helper->Send('http://sample-host.com/sample.xml');

The Send method has a second optional parameter, which indicates that the connection should be terminated right after getting the contents of the document. By default, it's set to "true", i.e. terminate connection.

Разборка XML документа

В классе kXMLHelper реализован удобный механизм разбора XML документа. Сначала следует создать экземпляр класса.

$xml_helper =& $this->Application->recallObject('kXMLHelper');
/* @var $xml_helper kXMLHelper */

Далее, начать обработку полученного ранее XML документа.

$root_node =& $xml_helper->Parse($file_contents);

В результате будет возвращена древовидная структура объектов, в которой все объекты связаны при помощи ссылок. Содержимое каждого объекта класса kXMLNode выглядит так:

kxmlnode Object (
	[Name] => xml_element_name
	[Attributes] => Array
	(
		[1st_attribute_name] => 1st_attribute_value
		[2nd_attribute_name] => 2nd_attribute_value
		...
	)
	[Children] => Array
	(
		[0] =>	kxmlnode Object
		[1] =>	kxmlnode Object
		...
	)
	[Data] => text_value_that_this_XML_element_encapsulates
	[firstChild] => kxmlnode Object
	[lastChild] => kxmlnode Object
	[Parent] =>  kxmlnode Object
	[Position] => 1
)
Image:Tipbox Icon.gif Всё ниже приведённое описание содержания объекта класса kXMLNode будет основано на примере, приведённом в начале статьи.

Переменной $root_node будет присвоен родительский (root) объект, т.е. это объект xml-элемента "our_document". Атрибут "Children" (private) содержит массив всех дочерних элементов текущего элемента. В данном примере ими являются два элемента - "some_tag" и "planet_earth". У последнего - два дочерних элемента "continent". Важно понимать, что элементы массива - точно такие же объекты, как и текущий. У них, в свою очередь, могут быть свои дочерние элементы, и так далее.

У каждого элемента есть атрибут Position. Это - порядковый номер элемента среди соседних элементов (элементов того же уровня, например - "country" Canada и "country" USA). Атрибуты "firstChild" и "lastChild" содержат первый и последний (с точки зрения его Position) дочерний элемент соответственно.

Для последующей обработки полученной информации используется методы и атрибуты именно класса kXMLNode.

Практическое использование kXMLHelper

Ниже приведён код, который распечатает все страны описанного выше XML-документа.

$root_node =& $xml_helper->Parse($xml_data);
/* @var $root_node kXMLNode */
 
// Getting first continent node
$continent_node =& $root_node->FindChild('continent');
 
// Cycling through it and all the rest continent nodes
do {
	// Getting first country node
	$country_node =& $continent_node->firstChild;
 
	// Cycling through it and all the rest continent nodes
    	do {
        	echo $continent_node->Attributes['ID'] . ' - ' . trim($continent_node->Data) . ': ' . $country_node->Attributes['ID'] . ' - ' . trim($country_node->Data) . '<br/>';
    	} while ($country_node =& $country_node->NextSibling());
} while ($continent_node =& $continent_node->NextSibling());

В переменной $continent_node сохраняется первый найденный объект "continent", т.е. - "North America". В первом цикле перебираются континенты. Для перехода к элементу того же уровня используется метод NextSibling (есть противоположный ему метод PrevSibling). Из континента выбирается первая страна. Внутренний цикл перебирает все страны данного континента и делает вывод в приведённом ниже формате. Как можно заметить, текстовое значение элементов доступно через атрибут "Data".

1 - North America: 1 - Canada
1 - North America: 2 - USA
2 - Europe: 1 - Estonia
2 - Europe: 2 - Latvia
2 - Europe: 3 - Lithuania

Методы класса "kXMLNode"

Ниже приведены public методы класса kXMLNode. Все методы рассчитаны на чтение данных, но не на их запись.

метод описание
&FindChild($name) Возвращает первый встретившийся элемент-потомок с указанным именем. Работает рекурсивно, до самого последнего уровня.
FindChildValue($name, $attr=null) Возвращает либо значение элемента-потомка (если задано только его имя), либо один из атрибутов (если явно указан).
&GetChildByPosition($position) Возвращает дочерний элемент, который находится по указанной позиции.
GetXML() Генерирует и возвращает XML-документ, построенный от текущего элемента. Актуально при предыдущем изменении структуры и не только.