Search code examples
phpxmlsimplexmlassociative-array

Create XML from associative array, and keep references to namespaces in XML tags


I'm having some trouble converting an array to an XML object, and keeping the associative keys from the array as tags in the created XML.

This is my code.

function to_xml($object, array $data) {
    foreach ($data as $key => $value) {
        if (is_array($value)) {
            $new_object = $object->addChild($key);
            to_xml($new_object, $value);
        } else {
            $object->addChild($key, $value);
        }
    }
}

$my_array = [
    "cbc:ID" => "myId",
    "cbc:IssueDate" => "2022-07-13",
    "cbc:DueDate" => "2022-07-20",
    "cbc:InvoiceTypeCode" => "380",
    "cbc:DocumentCurrencyCode" => "USD",
    "cac:InvoicePeriod" => [
        "cbc:DescriptionCode" => "35"
    ],
    "cac:ContractDocumentReference" => [
        "cbc:ID" => "123-456/22"
    ]
];

$xml = simplexml_load_string('<Invoice xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"/>','SimpleXMLElement',LIBXML_NOCDATA | LIBXML_NOBLANKS);

to_xml($xml, $my_array);

Header('Content-type: text/xml;charset=utf-8');
echo $xml->asXML();

This outputs:

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
  <ID>myID</ID>
  <IssueDate>2022-07-13</IssueDate>
  <DueDate>2022-07-20</DueDate>
  <InvoiceTypeCode>380</InvoiceTypeCode>
  <DocumentCurrencyCode>USD</DocumentCurrencyCode>
  <InvoicePeriod>
    <DescriptionCode>35</DescriptionCode>
  </InvoicePeriod>
  <ContractDocumentReference>
    <ID>123-456/22</ID>
  </ContractDocumentReference>
</Invoice>

What I need it to do is to preserve the keys as they were in the array, and use them in the exact same format as tags in the XML, like so (shortened, for simplicity's sake):

  <cbc:ID>myID</cbc:ID>
  <cbc:IssueDate>2022-07-13</cbc:IssueDate>
  <cbc:DueDate>2022-07-20</cbc:DueDate>
  <cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
  <cbc:DocumentCurrencyCode>USD</cbc:DocumentCurrencyCode>

I am aware that I could do something along the lines of:

$object->addChild($tagName, $value, $schemaReference);

for example:

$object->addChild("ID", "myID", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2");

to get what I need.

However, I was wondering whether there's a native way (for lack of a better word) of doing this. By native, I mean some built-in functionality of SimpleXML (or DOM, if SimpleXML is not the right tool for this).


Solution

  • It's kind of hacky, but you can modify the keys in $my_array to be cbc:cbc:ID, etc. When calling addChild, it will only omit one set of the prefix.

    $my_array = [
        "cbc:cbc:ID" => "myId",
        "cbc:cbc:IssueDate" => "2022-07-13",
        "cbc:cbc:DueDate" => "2022-07-20",
        "cbc:cbc:InvoiceTypeCode" => "380",
        "cbc:cbc:DocumentCurrencyCode" => "USD",
        "cac:cac:InvoicePeriod" => [
            "cbc:cbc:DescriptionCode" => "35"
        ],
        "cac:cac:ContractDocumentReference" => [
            "cbc:cbc:ID" => "123-456/22"
        ]
    ];