Skip to content

Commit

Permalink
Merge pull request #27 from veewee/decode-node
Browse files Browse the repository at this point in the history
Introduce element_decode
  • Loading branch information
veewee authored Feb 11, 2022
2 parents 28e9ee1 + e5e05cf commit 9c4e8c1
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 1 deletion.
30 changes: 30 additions & 0 deletions docs/encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ $xml = xml_encode($data, utf8(), pretty_print());
The encoding components consist out of following functions

- [document_encode](#document_encode): Encodes an array into a DOM [Document](dom.md).
- [element_decode](#element_decode): Decodes a DOMElement into an array.
- [typed](#typed): Decodes an XML string into a defined well-typed shape.
- [xml_decode](#xml_decode): Decodes an XML string into an array
- [xml_encode](#xml_encode): Encodes an array into an XML string
Expand Down Expand Up @@ -63,6 +64,35 @@ In the example above, we tell the DOM document to be UTF8 and pretty printed.

More information about [the PHP format can be found here](#php-format).

### element_decode

Decode a DOMElement into an array.

```php
use VeeWee\Xml\Dom\Document;
use VeeWee\Xml\Dom\Traverser\Visitor\RemoveNamespaces;
use function VeeWee\Xml\Dom\Configurator\traverse
use function VeeWee\Xml\Encoding\element_decode;

$doc = Document::fromXmlFile($file);
$element = $doc->xpath()->querySingle('//catalog:items/products[0]/item')

$data = element_decode($element, traverse(new RemoveNamespaces()));

// $data = ['name' => 'x', 'category' => 'z'];
```

Works similar to `xml_encode`.
It takes any DOMElement as input and transforms this element to a decoded XML array.
This way, you can use the DOM Document as a starting base to query data, but transform a resulting XML element into an array.

:exclamation: Since ext-dom reimports the node into a fresh document, namespaces might have been moved on a parent element.

Besides the element, you can apply any [DOM configurator](dom.md#configurators) you please.
In the example above, we run an XSD validator before parsing the XML into an array!

More information about [the PHP format can be found here](#php-format).

#### xml_decode

Decode transforms an XML string into an array.
Expand Down
4 changes: 3 additions & 1 deletion src/Xml/Encoding/Internal/Decoder/Builder/namespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ function namespaces(DOMElement $element): array
return filter([
'@namespaces' => xmlns_attributes_list($element)->reduce(
static fn (array $namespaces, DOMNameSpaceNode $node)
=> merge($namespaces, [(string) $node->prefix => $node->namespaceURI]),
=> $node->namespaceURI
? merge($namespaces, [(string) $node->prefix => $node->namespaceURI])
: $namespaces,
[]
),
]);
Expand Down
30 changes: 30 additions & 0 deletions src/Xml/Encoding/element_decode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace VeeWee\Xml\Encoding;

use DOMDocument;
use DOMElement;
use VeeWee\Xml\Dom\Document;
use VeeWee\Xml\Encoding\Exception\EncodingException;
use function VeeWee\Xml\Dom\Locator\document_element;
use function VeeWee\Xml\Encoding\Internal\Decoder\Builder\element;
use function VeeWee\Xml\Encoding\Internal\wrap_exception;

/**
* @param list<callable(DOMDocument): DOMDocument> $configurators
*
* @throws EncodingException
*/
function element_decode(DOMElement $element, callable ... $configurators): array
{
return wrap_exception(
static function () use ($element, $configurators): array {
$doc = Document::fromXmlNode($element, ...$configurators);
$root = $doc->locate(document_element());

return element($root);
}
);
}
1 change: 1 addition & 0 deletions src/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
require_once __DIR__.'/Xml/Encoding/Internal/Encoder/Builder/root.php';
require_once __DIR__.'/Xml/Encoding/Internal/wrap_exception.php';
require_once __DIR__.'/Xml/Encoding/document_encode.php';
require_once __DIR__.'/Xml/Encoding/element_decode.php';
require_once __DIR__.'/Xml/Encoding/typed.php';
require_once __DIR__.'/Xml/Encoding/xml_decode.php';
require_once __DIR__.'/Xml/Encoding/xml_encode.php';
Expand Down
23 changes: 23 additions & 0 deletions tests/Xml/Encoding/EncodingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@

use JsonSerializable;
use PHPUnit\Framework\TestCase;
use VeeWee\Xml\Dom\Document;
use VeeWee\Xml\Encoding\Exception\EncodingException;
use VeeWee\Xml\Encoding\XmlSerializable;
use function Psl\Fun\identity;
use function VeeWee\Xml\Dom\Locator\document_element;
use function VeeWee\Xml\Encoding\document_encode;
use function VeeWee\Xml\Encoding\element_decode;
use function VeeWee\Xml\Encoding\xml_decode;
use function VeeWee\Xml\Encoding\xml_encode;

final class EncodingTest extends TestCase
{
/**
* @dataProvider provideBidirectionalCases
* @dataProvider provideRiskyBidirectionalCases
* @dataProvider provideEncodingOnly
*/
public function test_it_encodes(string $xml, array $data)
Expand All @@ -27,6 +31,7 @@ public function test_it_encodes(string $xml, array $data)

/**
* @dataProvider provideBidirectionalCases
* @dataProvider provideRiskyBidirectionalCases
* @dataProvider provideEncodingOnly
*/
public function test_it_encodes_to_document(string $xml, array $data)
Expand All @@ -37,6 +42,7 @@ public function test_it_encodes_to_document(string $xml, array $data)

/**
* @dataProvider provideBidirectionalCases
* @dataProvider provideRiskyBidirectionalCases
* @dataProvider provideDecodingOnly
*/
public function test_it_decodes(string $xml, array $data)
Expand All @@ -45,6 +51,19 @@ public function test_it_decodes(string $xml, array $data)
static::assertSame($data, $actual);
}

/**
* @dataProvider provideBidirectionalCases
* @dataProvider provideDecodingOnly
*/
public function test_it_decodes_from_element(string $xml, array $data)
{
$doc = Document::fromXmlString($xml);
$element = $doc->locate(document_element());

$actual = element_decode($element, identity());
static::assertSame($data, $actual);
}

/**
* @dataProvider provideInvalidXml
*/
Expand Down Expand Up @@ -153,6 +172,10 @@ public function provideBidirectionalCases()
]
]
];
}

public function provideRiskyBidirectionalCases()
{
yield 'namespaced' => [
'xml' => <<<EOXML
<root xmlns="http://rooty.root" xmlns:test="http://testy.test">
Expand Down

0 comments on commit 9c4e8c1

Please sign in to comment.