eliashaeussler / valinor-xml
XML source for cuyz/valinor
Installs: 105 650
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 2
pkg:composer/eliashaeussler/valinor-xml
Requires
- php: ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0
 - ext-mbstring: *
 - cuyz/valinor: ^1.3 || ^2.0
 - mtownsend/xml-to-array: ^2.0
 
Requires (Dev)
- armin/editorconfig-cli: ^1.8 || ^2.0
 - eliashaeussler/php-cs-fixer-config: ^2.0
 - eliashaeussler/phpstan-config: ^2.0
 - eliashaeussler/rector-config: ^3.0
 - ergebnis/composer-normalize: ^2.30
 - phpstan/extension-installer: ^1.2
 - phpstan/phpstan-phpunit: ^2.0
 - phpunit/phpunit: ^10.2 || ^11.0 || ^12.0
 
README
XML source for cuyz/valinor
A Composer library that provides an additional XML source for use with
the popular cuyz/valinor library.
This allows to easily map XML files or contents to any signature supported
by Valinor, e.g. objects or special array shapes. It leverages the
mtownsend/xml-to-array
library to convert raw XML to a reusable array structure.
🔥 Installation
composer require eliashaeussler/valinor-xml
⚡ Usage
Given the following XML:
<?xml version="1.0" encoding="UTF-8"?> <person> <name>Dr. Zane Stroman</name> <address> <street>439 Karley Loaf</street> <postcode>17916</postcode> <city>West Judge</city> <country>Falkland Islands (Malvinas)</country> </address> <contact> <phone>827-986-5852</phone> </contact> </person>
These are the resulting classes:
final readonly class Address { public function __construct( public string $street, public string $postcode, public string $city, public string $country, ) {} } final readonly class Contact { public function __construct( public string $phone, ) {} } final readonly class Person { public function __construct( public string $name, public Address $address, public Contact $contact, ) {} }
Mapping from XML string
In order to map the given XML to the Person class, you need
to follow these three steps:
- Create a new mapper as written in Valinor's documentation
 - Parse and prepare your XML using the shipped 
XmlSource - Use the mapper to map your XML to the 
Personclass 
use CuyZ\Valinor; use EliasHaeussler\ValinorXml; $mapper = (new Valinor\MapperBuilder())->mapper(); $source = ValinorXml\Mapper\Source\XmlSource::fromXmlString($xml); $person = $mapper->map(Person::class, $source); // instanceof Person
The resulting object will look something like this:
object(Person)#180 (3) {
  ["name"]=>
  string(16) "Dr. Zane Stroman"
  ["address"]=>
  object(Address)#135 (4) {
    ["street"]=>
    string(15) "439 Karley Loaf"
    ["postcode"]=>
    string(5) "17916"
    ["city"]=>
    string(10) "West Judge"
    ["country"]=>
    string(27) "Falkland Islands (Malvinas)"
  }
  ["contact"]=>
  object(Contact)#205 (1) {
    ["phone"]=>
    string(12) "827-986-5852"
  }
}
Mapping from XML file
The XML can also be read from an external file:
use CuyZ\Valinor; use EliasHaeussler\ValinorXml; $mapper = (new Valinor\MapperBuilder())->mapper(); $source = ValinorXml\Mapper\Source\XmlSource::fromXmlFile($file); $person = $mapper->map(Person::class, $source); // instanceof Person
Convert nodes to collections
Sometimes it might be necessary to always convert XML nodes to collections. Given the following XML:
<?xml version="1.0" encoding="UTF-8"?> <community> <member><!-- ... --></member> <!-- NOTE: There's only one member --> </community>
Let's assume you want to map this XML to the following class:
final readonly class Community { /** * @param list<Person> $member */ public function __construct( public array $member, ) {} }
You will recognize that this does not work as expected when using the above mapping method:
use CuyZ\Valinor; use EliasHaeussler\ValinorXml; $mapper = (new Valinor\MapperBuilder())->mapper(); $source = ValinorXml\Mapper\Source\XmlSource::fromXmlFile($file); $person = $mapper->map(Community::class, $source); // throws exception
It will instead throw an exception like this:
CuyZ\Valinor\Mapper\TypeTreeMapperError: Could not map type `Community` with value array{member: array{…}}.
This is because the XML converter does not know whether <member> should
be a collection or if it's just a "normal" node. That's why the XmlSource
provides an appropriate method to convert such nodes to collections:
use CuyZ\Valinor; use EliasHaeussler\ValinorXml; $mapper = (new Valinor\MapperBuilder())->mapper(); $source = ValinorXml\Mapper\Source\XmlSource::fromXmlFile($file) ->asCollection('member') ; $person = $mapper->map(Community::class, $source); // instanceof Community
The resulting object will look something like this:
object(Community)#76 (1) {
  ["member"]=>
  array(1) {
    [0]=>
    object(Person)#126 (3) {
      ["name"]=>
      string(16) "Dr. Zane Stroman"
      ["address"]=>
      object(Address)#170 (4) {
        ["street"]=>
        string(15) "439 Karley Loaf"
        ["postcode"]=>
        string(5) "17916"
        ["city"]=>
        string(10) "West Judge"
        ["country"]=>
        string(27) "Falkland Islands (Malvinas)"
      }
      ["contact"]=>
      object(Contact)#252 (1) {
        ["phone"]=>
        string(12) "827-986-5852"
      }
    }
  }
}
However, this is only relevant if only one node of the collection exists in your XML. If the XML contains more than one node, the XML converter properly converts them to a collection:
use CuyZ\Valinor; use EliasHaeussler\ValinorXml; $xml = <<<XML <?xml version="1.0" encoding="UTF-8"?> <community> <member><!-- ... --></member> <member><!-- ... --></member> <member><!-- ... --></member> </community> XML; $mapper = (new Valinor\MapperBuilder())->mapper(); $source = ValinorXml\Mapper\Source\XmlSource::fromXmlString($xml); $person = $mapper->map(Community::class, $source); // instanceof Community
🧑💻 Contributing
Please have a look at CONTRIBUTING.md.
⭐ License
This project is licensed under GNU General Public License 3.0 (or later).