0

I am using SimpleXML to construct XML to be used as XBRL.

Question:

How can I implement namespace on child element the correct way, using SimpleXML as base?

Observations:

  • Missing namespace (thus there are no [xbrli:xbrl], [se-cd-base:CompanyName].

  • Missing encoding string.


My code:

<?php

$test_array = [
  'TheCompany' => 'CompanyName'
];

$xml = new SimpleXMLElement('<xbrli/>');
array_walk_recursive($test_array, array ($xml, 'addChild'));
print $xml->asXML();

Result

<?xml version="1.0"?>
<xbrli>
  <CompanyName>
    TheCompany
  </CompanyName>
</xbrli>

Wanted result (XBRL)

<?xml version="1.0" encoding="UTF-8"?>

  <xbrli:xbrl xmlns:link = "http://www.xbrl.org/2003/linkbase">

  <link:schemaRef
    xlink:type="simple"
    xlink:href="http://xbrl.taxonomier.se/se/fr/gaap/k2/risbs/2017-09-30/se-k2-risbs-2017-09-30.xsd"/
    >

  <se-cd-base:CompanyName
    contextRef="period0">
    TheCompany
  </se-cd-base:CompanyName>

</xbrli:xbrl>
Toolbox
  • 2,333
  • 12
  • 26
  • The wanted result is not strictly valid XML. There is no definition of `xbrli` or `se-cd-base` namespace. Nevertheless, [SimpleXMLElement constructor](https://www.php.net/manual/en/simplexmlelement.construct.php) has `$ns` and `$is_prefix` parameters that you can use to set the namespace. – Furgas Feb 06 '20 at 10:46
  • @Furgas Updated the question that the wanted result is (XBRL). – Toolbox Feb 06 '20 at 10:48
  • Actually, `$ns` and `$is_prefix` if working differently than I thought (that's why I prefer using [DOM](https://www.php.net/manual/en/book.dom.php) over SimpleXML). Refer to this [answer](https://stackoverflow.com/a/43561160/170403) for clues. – Furgas Feb 06 '20 at 11:00

1 Answers1

1

When using namespaces in an XML document, you need to think about three things:

  • The namespace URI. This is the globally unique identifier that tools will recognise as being the same namespace (the URI doesn't have to point anywhere, it's just a way of organising who "owns" the identifier).
  • The local prefix. This is an arbitrary string which a particular document, or even part of a document, associates with a particular namespace URI, basically just to keep things more compact. This is the part before the : in a tag like <xbrli:xbrl>. There is also a default namespace for each part of a document, for elements with no prefix.
  • The element or attribute name within that namespace. This is the part after the : in a tag like <xbrli:xbrl>.

I mention all this to understand why the sample XML you provide is invalid, because it looks like you want to use four namespaces:

  1. Namespace http://www.xbrl.org/2003/linkbase which you have given the local prefix link
  2. An unknown namespace which you have given the local prefix xbrli; I'll call this one http://example.org/xbrli
  3. An unknown namespace which you have given the local prefix se-cd-base; I'll call this one http://example.org/se-cd-base
  4. An unknown namespace which you have given the local prefix xlink; I'll call this one http://example.org/xlink (unless this was a typo and should have been another reference to http://www.xbrl.org/2003/linkbase?)

Now let's try to construct a valid version of your XML using SimpleXML...

First, we need to create the root element, which is in the http://example.org/xbrli namespace; SimpleXML doesn't have a way to create a document without any nodes, so we have to write the first node by hand and parse it:

// Using xbrli as prefix for http://example.org/xbrli
$xml = new SimpleXMLElement('<xbrli xmlns="http://example.org/xbrli"/>');
// Or using http://example.org/xbrli as the default namespace for the document
$xml = new SimpleXMLElement('<xbrli xmlns="http://example.org/xbrli"/>');

Next, we want a child element schemaRef in the http://www.xbrl.org/2003/linkbase namespace. We do this by passing the namespace as the third parameter to addChild, and including the prefix in the element name if we want one:

// Using link as the prefix for http://www.xbrl.org/2003/linkbase
$schemaRef = $xml->addChild('link:schemaRef', null, 'http://www.xbrl.org/2003/linkbase');
// Or making http://www.xbrl.org/2003/linkbase the default namespace for this section
$schemaRef = $xml->addChild('schemaRef', null, 'http://www.xbrl.org/2003/linkbase');

Next, we want to add attributes in the http://example.org/xlink namespace. The arguments to addAttribute are similar to the above, but the prefix is mandatory:

$schemaRef->addAttribute('xlink:type', 'simple', 'http://example.org/xlink');
$schemaRef->addAttribute('xlink:href', 'http://xbrl.taxonomier.se/se/fr/gaap/k2/risbs/2017-09-30/se-k2-risbs-2017-09-30.xsd', 'http://example.org/xlink');

Now repeat for the CompanyName element; note that unprefixed attributes have a rather odd definition in the namespaces spec but we'll keep it as per your example:

$CompanyName = $xml->addChild('se-cd-base:CompanyName', 'The Company', 'http://example.org/se-cd-base');
// Again, we can declare a default namespace rather than a prefix:
$CompanyName = $xml->addChild('CompanyName', 'The Company', 'http://example.org/se-cd-base');
// Attribute with no namespace
$CompanyName->addAttribute('contextRef', 'period0');

Now put it all together, and check with echo $xml->asXML(); and we get something like this (whitespace added manually):

<?xml version="1.0"?>
<xbrli xmlns="http://example.org/xbrli">
    <link:schemaRef
        xmlns:link="http://www.xbrl.org/2003/linkbase" 
        xmlns:xlink="http://example.org/xlink"
        xlink:type="simple"
        xlink:href="http://xbrl.taxonomier.se/se/fr/gaap/k2/risbs/2017-09-30/se-k2-risbs-2017-09-30.xsd"
    />
    <se-cd-base:CompanyName 
        xmlns:se-cd-base="http://example.org/se-cd-base" 
        contextRef="period0"
    >The Company
    </se-cd-base:CompanyName>
</xbrli>

Or an equivalent document using default namespaces rather than prefixes:

<?xml version="1.0"?>
<xbrli xmlns="http://example.org/xbrli">
    <schemaRef 
        xmlns="http://www.xbrl.org/2003/linkbase"
        xmlns:xlink="http://example.org/xlink"
        xlink:type="simple"
        xlink:href="http://xbrl.taxonomier.se/se/fr/gaap/k2/risbs/2017-09-30/se-k2-risbs-2017-09-30.xsd"
    />
    <CompanyName
        xmlns="http://example.org/se-cd-base"
        contextRef="period0"
    >
    The Company
    </CompanyName>
</xbrli>
IMSoP
  • 89,526
  • 13
  • 117
  • 169