XML-Array Conversion Structure
The Derafu XML library uses a specific convention for representing the structure of XML documents as PHP arrays and vice versa. Understanding this structure is essential for effectively using the encoding and decoding features. For what is kept when an XML becomes an array and the array becomes an XML again, see Round Trip and Fidelity.
Basic Structure
At its most basic level, an XML element with a value is represented as a key-value pair in the array:
XML:
<element>value</element>
Array:
['element' => 'value']
Nested Elements
Nested elements are represented as nested arrays:
XML:
<parent>
<child>value</child>
</parent>
Array:
[
'parent' => [
'child' => 'value'
]
]
Attributes
XML attributes are handled using the special @attributes key:
XML:
<element id="123" type="example">value</element>
Array:
[
'element' => [
'@attributes' => [
'id' => '123',
'type' => 'example'
],
'@value' => 'value'
]
]
Note the use of @value to hold the element’s text content when the element also has attributes (or child elements or comments).
Repeated Elements
Elements with the same name are collected into arrays:
XML:
<parent>
<child>value1</child>
<child>value2</child>
<child>value3</child>
</parent>
Array:
[
'parent' => [
'child' => [
'value1',
'value2',
'value3'
]
]
]
Complex Structures
The conventions can be combined to represent complex XML structures:
XML:
<root>
<items>
<item id="1">
<name>Item 1</name>
<price currency="USD">19.99</price>
</item>
<item id="2">
<name>Item 2</name>
<price currency="EUR">29.99</price>
</item>
</items>
<summary total="2">Multiple items</summary>
</root>
Array:
[
'root' => [
'items' => [
'item' => [
[
'@attributes' => ['id' => '1'],
'name' => 'Item 1',
'price' => [
'@attributes' => ['currency' => 'USD'],
'@value' => '19.99'
]
],
[
'@attributes' => ['id' => '2'],
'name' => 'Item 2',
'price' => [
'@attributes' => ['currency' => 'EUR'],
'@value' => '29.99'
]
]
]
],
'summary' => [
'@attributes' => ['total' => '2'],
'@value' => 'Multiple items'
]
]
]
Text
The text of an element is exactly the one that the XML has, with its spaces. The library does not trim it: what to do with the spaces is for whoever uses the value.
XML:
<name> Acme Inc. </name>
Array:
['name' => ' Acme Inc. ']
CDATA sections and entities are the text that they are (<![CDATA[x < y]]> is 'x < y'). The spaces and the line breaks that only format the XML (the ones between elements) are not text.
When an element has child elements or comments and also text, the text is in parts, and the parts are joined with a space, without the spaces around each part (see What the array does not keep).
Namespaces
The namespaces that an element declares are in its @attributes, as they are written in the XML: xmlns for the default one and xmlns:prefix for the others. They go first, sorted by name, and before the other attributes. A namespace is only where it is declared, not in the elements that inherit it.
XML:
<Invoice xmlns="urn:example:invoice" xmlns:cbc="urn:example:basic" id="1">
<cbc:ID>F-1</cbc:ID>
</Invoice>
Array:
[
'Invoice' => [
'@attributes' => [
'xmlns' => 'urn:example:invoice',
'xmlns:cbc' => 'urn:example:basic',
'id' => '1',
],
'cbc:ID' => 'F-1',
]
]
The names of elements and attributes keep their prefix (cbc:ID, xsi:schemaLocation). An element that leaves the default namespace without URI has 'xmlns' => ''. With the namespaces in the array, the XML that is created from it has the same elements in the same namespaces.
Comments
The comments of an element are in its @comment key, as they are written (with their spaces): a text if there is one, a list of texts if there are several.
XML:
<element>
<!-- first -->
<child>value</child>
</element>
Array:
[
'element' => [
'@comment' => ' first ',
'child' => 'value',
]
]
Special Cases
Empty Elements
An element without content is an empty string, whether it is written <element/> or <element></element>: it is the value of an empty element in the data model of XML.
XML:
<element></element>
Array:
['element' => '']
When creating XML, the same empty string creates an empty element. An element is a null only when it is skipped (see below).
Skipping Elements
When creating XML, certain values cause an element to be skipped:
[
'included' => 'This will be in the XML',
'excluded' => null, // This will be skipped.
'alsoExcluded' => false, // This will be skipped.
'emptyArraySkipped' => [] // This will be skipped.
]
Array to XML Encoding Rules
When using XmlEncoder to create XML from arrays, these rules apply:
- Simple key-value pairs become elements with text content.
- Nested arrays become nested elements.
- The special key
@attributesdefines attributes for the parent element. The namespaces (xmlns,xmlns:prefix) are declared the same way. - The special key
@valuedefines the text content of the parent element. It is written before the child elements and the comments, wherever the key is in the array. - The special key
@commentadds a comment (or a list of them) to the parent element. - Arrays of values create multiple elements with the same name.
- Null, false, and empty arrays are skipped (not included in the XML).
- Empty strings (’’) create empty elements.
- The array has exactly one element at its first level: it is the root element of the XML.
- A value is written as text if it is a string, a number, a boolean or an object that can be a string. Anything else is not valid.
- The names of elements and attributes are text. A number is the position of a value in a list, not a name.
What is not valid (a second root element, an attribute that is an array, a name that is not text, a value that can not be written as text, a text outside of an element, a comment that has two hyphens in a row or ends with a hyphen) throws an XmlEncoderException.
The XML has the encoding of the document that is returned: it is UTF-8 unless it is changed with setEncoding().
The document that is returned has the namespaces that the array declares: its elements and its attributes are in them, as in a document that is loaded from the XML. XPath queries with namespaces and C14N() work in it without writing the XML and loading it again. A prefix that the array does not declare is a name without namespace.
XML to Array Decoding Rules
When using XmlDecoder to convert XML to arrays, these rules apply:
- Elements become keys in the array.
- Text content becomes the value of the key.
- Attributes are collected under the
@attributeskey, with the namespaces that the element declares. - When the element has attributes, or child elements or comments, its text content is stored under the
@valuekey. - Multiple elements with the same name are collected into arrays.
- Empty elements become empty strings.
- Comments are collected under the
@commentkey. - Text is exactly the one of the XML, with its spaces.
- Complex nested structures are preserved in the array hierarchy.
What the Array Does Not Keep
An array with names as keys can not say some things that an XML can. The array that is decoded from an XML has its data, and what follows is the part that does not come back when it is encoded again:
-
The order among child elements with different names, when one of them is repeated. The repeated elements are a list under their name.
<r><a>1</a><b>2</b><a>3</a></r> <!-- ['r' => ['a' => ['1', '3'], 'b' => '2']] -->is encoded as
<r><a>1</a><a>3</a><b>2</b></r>. -
The place of the text and of the comments among the child elements. The text that is mixed with elements or comments is joined in
@value, and the comments of an element are together, in the place of the first one.<p>Hello <b>big</b> world</p> <!-- ['p' => ['@value' => 'Hello world', 'b' => 'big']] -->is encoded as
<p>Hello world<b>big</b></p>. It is a structure for data. It is not for documents that mix text and markup, as HTML does. -
What is outside of the root element: comments, processing instructions (
<?xml-stylesheet ... ?>) and the document type. -
The processing instructions inside of the elements.
-
The declaration of the XML: the version and
standalone. The encoding is the one of the document (getEncoding()andsetEncoding()), not of the array. -
The entities that a document type defines. A reference to one of them has no text in the array.
-
How the text was written: CDATA sections, character references (
é), the quotes of the attributes,<a/>or<a></a>, and the spaces that format the XML.
Usage Examples
Creating Complex XML
use Derafu\Xml\Service\XmlEncoder;
$data = [
'invoice' => [
'@attributes' => [
'id' => 'INV-2025-001',
'date' => '2025-03-05'
],
'customer' => [
'name' => 'Acme Inc.',
'address' => [
'street' => '123 Main St',
'city' => 'Anytown',
'zipcode' => '12345'
]
],
'items' => [
'item' => [
[
'@attributes' => ['sku' => 'PROD1'],
'description' => 'Product One',
'quantity' => '2',
'price' => '10.99'
],
[
'@attributes' => ['sku' => 'PROD2'],
'description' => 'Product Two',
'quantity' => '1',
'price' => '24.99'
]
]
],
'total' => '46.97'
]
];
$encoder = new XmlEncoder();
$xmlDocument = $encoder->encode($data);
echo $xmlDocument->saveXml();
Processing XML into Arrays
use Derafu\Xml\Service\XmlDecoder;
$xmlString = '
<catalog>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer\'s Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2025-01-15</publish_date>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2025-02-20</publish_date>
</book>
</catalog>';
$document = new \Derafu\Xml\XmlDocument();
$document->loadXml($xmlString);
$decoder = new XmlDecoder();
$array = $decoder->decode($document);
// Access data directly.
$firstBookTitle = $array['catalog']['book'][0]['title']; // "XML Developer's Guide"
$secondBookPrice = $array['catalog']['book'][1]['price']; // "5.95"
Tips for Working with the Array Structure
- Plan Your Structure: When creating XML, plan your array structure carefully to match the desired XML output.
- Handle Repeated Elements: Always use numeric arrays for repeated elements with the same name.
- Use
@attributesand@value: Remember to use these special keys when working with elements that have both attributes and text content. - Validate Your Output: Always validate the generated XML against your schema if one exists.
- Check for Empty Values: When processing arrays from XML, an empty element is an empty string (
''), notnull. - Clean the Text Where You Use It: The text is not trimmed. If a value must have no spaces around it, trim it where it is used.