PHP SimpleXMLElement addAttribute namespaces syntax - php

I'm working with SimpleXMLElement for the first time and need to generate a line in my XML as follows:
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
I haven't used addAttribute with namespaces before and can't get the correct syntax working here - I've started with this:
$node = new SimpleXMLElement('< Product ></Product >');
$node->addAttribute("xmlns:", "xsd:", 'http://www.w3.org/2001/XMLSchema-instance');
but can't work out how to correct this for the appropriate syntax to generate the desired output?

solution 1: add a prefix to the prefix
<?php
$node = new SimpleXMLElement('<Product/>');
$node->addAttribute("xmlns:xmlns:xsi", 'http://www.w3.org/2001/XMLSchema-instance');
$node->addAttribute("xmlns:xmlns:xsd", 'http://www.w3.org/2001/XMLSchema');
echo $node->asXML();
output:
<?xml version="1.0"?>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>
note: this is a workaround and actually doesn't set the namespace for the attribute, but just quite enough if you are going to echo / save to file the result
solution 2: put namespace directly in the SimpleXMLElement constructor
<?php
$node = new SimpleXMLElement('<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>');
echo $node->asXML();
output is the same as in solution 1
solution 3 (adds additional attribute)
<?php
$node = new SimpleXMLElement('<Product/>');
$node->addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance", "xmlns");
$node->addAttribute("xmlns:xsd", 'http://www.w3.org/2001/XMLSchema', "xmlns");
echo $node->asXML();
output adds additional xmlns:xmlns="xmlns"
<?xml version="1.0"?>
<Product xmlns:xmlns="xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"/>

Related

Add xml-stylesheet tag simpleXML php

Hy guys this is my simple code:
$xml = new SimpleXMLElement('<p:FatturazioneElettronica xmlns:p="http://microsoft.com/wsdl/types/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://dummy.com"/>');
$xml->addAttribute("versione","FPR12");
$FatturaElettronicaHeader = $xml->addChild('FatturaElettronicaHeader',null,'http://dummy.com');
the xml results is:
<p:FatturazioneElettronica xmlns:p="http://microsoft.com/wsdl/types/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://dummy.com" versione="FPR12">
<FatturaElettronicaHeader>
<DatiTrasmissione>
....
How can add at the top of my xml "xml-stylesheet"?
<?xml-stylesheet type="text/xsl" href="fatturapa_v1.2.xsl" ?>
SimpleXML isn't very good at doing anything other than simple things (think that's were the name is very apt). The only way I can think of doing it is to use DOMDocument, which provides a richer API and you should be able to do it as follows...
$xmlD = new DOMDocument( "1.0", "ISO-8859-15" );
$xmlD->appendChild($xmlD->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="fatturapa_v1.2.xsl"'));
$xmlD->appendChild($xmlD->importNode(dom_import_simplexml($xml)));
echo $xmlD->saveXML();
This creates a new DOMDocument instance and then adds a few things. First it uses createProcessingInstruction() to add the processing instruction for the style sheet. Then it imports the existing contents of your SimpleXML document (in $xml) and appends this to the end. The echo should give you a document something like...
<?xml version="1.0" encoding="ISO-8859-15"?>
<?xml-stylesheet type="text/xsl" href="fatturapa_v1.2.xsl"?>
<p:FatturazioneElettronica xmlns:p="http://microsoft.com/wsdl/types/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://dummy.com" versione="FPR12"/>

PHP get specific data from xml file

This is XML file.
<?xml version="1.0" encoding="utf-8"?>
<UW xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
<UWdata>
<List>
<IdProduct>1</IdProduct>
<ProductName>product</ProductName>
<ProductNameDE>product</ProductNameDE>
<ProductNameEN>product</ProductNameEN>
<Uf>1</Uf>
<PSIg>1</PSIg>
<Ug>1</Ug>
</List>
</UWdata>
</UW>
$lines_array=file($url);
$lines_string=implode('',$lines_array);
$xml=simplexml_load_string($lines_string) or die("Error: Cannot create object");
I try with this
echo $xml->UWdata[1]->ProductName;
But it doesn't return anything.I want to return Product name.
Sample code, Use simplexml_load_string
<?php
$a = '<?xml version="1.0" encoding="utf-8"?>
<UW xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
<UWdata>
<List>
<IdProduct>1</IdProduct>
<ProductName>product</ProductName>
<ProductNameDE>product</ProductNameDE>
<ProductNameEN>product</ProductNameEN>
<Uf>1</Uf>
<PSIg>1</PSIg>
<Ug>1</Ug>
</List>
</UWdata>
</UW>';
$xml=simplexml_load_string($a) or die("Error: Cannot create object");
echo ($xml->UWdata->List->ProductName);
?>
When you load the xml file using the php simplexml_load_file function to a variable. The veritable becomes an object.
<?php
$xml=simplexml_load_file("/path/to/the/file.xml");
?>
So, in your case, the $xml variable becomes a multi-level object where every elements of xml file are key of the object. Like: UWdata.
So, as $xml is a multi-level object, to access the element under UWdata, under List under ProductName, you have to code like bellow.
echo $xml->UWdata->List->ProductName."<br>";
Here,
UWdata is the key of $xml object.
List is the key of UWdata.
ProductName is the key of List.
Finally, you will get the value of key element ProductName = product
I modified your script and put the xml in an external file called testxml.xml, as it should be. Always separate the function and the data it's supposed to handle. I used your xml like this:
<?xml version="1.0" encoding="utf-8"?>
<UW xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/XMLSchema.xsd">
<UWdata>
<List>
<IdProduct>1</IdProduct>
<ProductName>productTEST</ProductName>
<ProductNameDE>product</ProductNameDE>
<ProductNameEN>product</ProductNameEN>
<Uf>1</Uf>
<PSIg>1</PSIg>
<Ug>1</Ug>
</List>
</UWdata>
</UW>
And with the following script it returns productTEST only.
$xmlstr = file_get_contents('./testxml.xml');
$xml = simplexml_load_string($xmlstr);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
echo $array['UWdata']['List']['ProductName'];
Hope this helps.
//edit:
While I do not know your project, you might want to take a foreach-approach if it is possible for your xml to contain more than one List element

how can i generate this XML format with this element

This is what the output should be:
<invoice:company this="1">
<invoice:transport from="7777777777" to="77777777777">
<invoice:via via="7777777777" id="1"/>
</invoice:transport>
</invoice:company>
But I am getting this:
<company this="1">
<transport from="7777777777" to="77777777777">
<via via="7777777777" id="1"/>
</transport>
</company>
I am using this as XML generator:
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><invoice>
</invoice>');
//child of invoice
$company= $xml->addChild('company');
//child of company
$transport = $processing->addChild('transport');
$transport->addAttribute('to','77777777777');
$transport->addAttribute('from','77777777777');
//child of transport
$via = $transport->addChild('via');
$via->addAttribute('id','1');
$via->addAttribute('via','77777777777');
$xml->saveXML();
$xml->asXML("company_001.xml");'
Why is ":" on the element tag? how can I do that? I need to have that too.
As mentioned in the comment, invoice: is the namespace of the elements in the document.
When creating an XML document with a namespace, you need to declare it. In the code below, in this I've done it in the initial document loaded into SimpleXMLElement. I don't know the correct definition of this namespace - so I've used "http://some.url" throughout (and all references need to be changed). If you don't define this namespace, SimpleXML will add it's own definition the first time you use it.
When adding the elements in, you can define which namespace they get added to, the third parameter of addChild is the namespace.
So...
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?>
<invoice xmlns:invoice="http://some.url">
</invoice>');
//child of invoice
$processing= $xml->addChild('company', "", "http://some.url");
//child of company
$transport = $processing->addChild('transport', "", "http://some.url");
$transport->addAttribute('to','77777777777');
$transport->addAttribute('from','77777777777');
//child of transport
$via = $transport->addChild('via', "", "http://some.url");
$via->addAttribute('id','1');
$via->addAttribute('via','77777777777');
echo $xml->asXML();
Produces (I've formated the output to help)...
<?xml version="1.0" encoding="utf-8"?>
<invoice xmlns:invoice="http://some.url">
<invoice:company>
<invoice:transport to="77777777777" from="77777777777">
<invoice:via id="1" via="77777777777" />
</invoice:transport>
</invoice:company>
</invoice>
As I'm not sure if this is the entire document your creating, there may need to be minor changes, but hope this helps.

php SimpleXML get full namespace attributes [duplicate]

I'd like to get the content of the attribute xsi:schemaLocation. It's works perfectly with getElementsByTagName in php (and foreach after) but it's ugly, right ?
How to get the same content with a simple Xpath query ?
Here a short example of the xml content :
<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0" creator="blabla" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd" xmlns="http://www.topografix.com/GPX/1/0">
...
</gpx>
Thanks!
Typically you need to register the namespaces you want to use with the XPath library first. Then you can query the attribute by including namespace prefix along with the name.
So let's assume you're using DOMXPath, you might register the following namespaces:
$xpath = new DOMXPath($doc);
$xpath->registerNamespace("xsi","http://www.w3.org/2001/XMLSchema-instance");
$xpath->registerNamespace("gpx", "http://www.topografix.com/GPX/1/0");
And then you can query the schemaLocation attribute with something like this:
$xpath->query("/gpx:gpx/#xsi:schemaLocation",$doc);
Using the SimpleXMLElement class you can easily get the attribute xsi:schemaLocation's value:
<?php
$xml = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0" creator="blabla" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd" xmlns="http://www.topografix.com/GPX/1/0">
</gpx>
XML;
$sxe = new SimpleXMLElement($xml);
$schemaLocation = $sxe->attributes('xsi', true)->schemaLocation;
echo (string) $schemaLocation;

Making the nodes to ignore namespaces (prefixes) after changing XML structure. PHP DOMDocument

Original XML (myfile.xml)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<blabla
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:blabla="http://www.w3.org/2000/blabla"
xmlns="http://www.w3.org/2000/blabla"
version="1.0">
<title>Hello there</title>
<metadata>
<rdf:RDF>
<cc:whtaat />
</rdf:RDF>
</metadata>
<sometag>
<anothertag id="anothertag1111">
<andanother id="yep" />
</anothertag >
</sometag>
</blabla>
The aim is adding a child straight under the document root node and "pushing" the "original" children under the new child:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<blabla
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:blabla="http://www.w3.org/2000/blabla"
xmlns="http://www.w3.org/2000/blabla"
version="1.0">
<magic>
<title>Hello there</title>
<metadata>
<rdf:RDF>
<cc:whtaat />
</rdf:RDF>
</metadata>
<sometag>
<anothertag id="anothertag1111">
<andanother id="yep" />
</anothertag >
</sometag>
</magic>
</blabla>
This php script does that
<?php
header("Content-type: text/xml");
// Create dom document
$doc = new DOMDocument();
$doc->load("myfile.xml");
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
// Get first child (blabla)
$blablaNode = $doc->firstChild;
// Crete magic element to hold all children in blabla
$magicElement = $doc->createElement('magic');
while($blablaNode->hasChildNodes()) {
// Remove child from blablaNode and append it into magicElement
$magicElement->appendChild($blablaNode->removeChild($blablaNode->firstChild));
}
// Append magicElement to blablaNode
$magicElement = $blablaNode->appendChild($magicElement);
echo $doc->saveXML();
?>
however the output is
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<blabla xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:blabla="http://www.w3.org/2000/blabla"
xmlns="http://www.w3.org/2000/blabla" version="1.0">
<magic>
<blabla:title xmlns:default="http://www.w3.org/2000/blabla">Hello there</blabla:title>
<blabla:metadata xmlns:default="http://www.w3.org/2000/blabla" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#">
<rdf:RDF>
<cc:whtaat/>
</rdf:RDF>
</blabla:metadata>
<blabla:sometag xmlns:default="http://www.w3.org/2000/blabla">
<blabla:anothertag id="anothertag1111">
<blabla:andanother id="yep"/>
</blabla:anothertag>
</blabla:sometag>
</magic>
</blabla>
So every node (that is in the "default" namespace) has "blaba" prefix attached to it
<blabla:title />
How to avoid that?
When inspecting the ongoings if changing the PHP to
while($blablaNode->hasChildNodes()) {
$removedChild = $blablaNode->removeChild($blablaNode->firstChild);
echo "(prefix for removed:".$removedChild->prefix.")";
$magicElement->appendChild($removedChild);
echo "(prefix for added:".$magicElement->lastChild->prefix.")";
}
echo is ...(prefix for removed:)(prefix for added:)(prefix for removed:)(prefix for added:default)...
Many thanks in advance!
P.S. This is sequel to this question thus "Or maybe someone has a much better solution in general for achieving the desirable result [adding magic node and pushing everything in it]" still applies...
Indeed, if "putting default namespace declaration first", as Josh Davis notes, the lookup prefix goes away. +1. But that's it as in the output...
...
<metadata xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#">
...
... the declarations still are there.
A clarification. I'm not the creator of those XML docs. Therefore checking the position of default namespace declaration... even if implemented it still wouldn't give the desirable result. And even if those declarations added by libxml should be there by standard, my task is not to validate conformance, but
- simply put all original childnodes, intact in their content (declarations, names values, attributes etc.), under that extra newly created container.
When you append those children, I guess that libxml looks for the first namespace declaration for "http://www.w3.org/2000/blabla" and finds "blabla". Now if you put your default namespace declaration first, it will find that the default namespace works and it will not prefix those nodes with blabla.
<blabla xmlns="http://www.w3.org/2000/blabla"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:blabla="http://www.w3.org/2000/blabla"
version="1.0">
Update
The issue is entirely cosmetic, but if you want to remove redundant namespace declarations, you can dump and reload your XML:
$xml = $doc->saveXML();
$doc = new DOMDocument;
$doc->loadXML($xml, LIBXML_NSCLEAN);
echo $doc->saveXML();
Attention if you reuse the $doc variable, it doesn't mean that stuff like $blablaNode will remain functional, it won't. The new $doc is a new document.
Oh, and it will also clean up redundant namespaces from the original document, possibly breaking that "keeping it intact" rule.
Oh, and I forgot to mention that you have to explicitely declare which namespace <magic/> is to be created into:
$magicElement = $doc->createElementNS('http://www.w3.org/2000/blabla', 'magic');

Categories