Add xml-stylesheet tag simpleXML php - 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"/>

Related

Remove stylesheet from XML with PHP

I'm tring to show some XML for Italian Electronic invoices received, using my custom stylesheet.xsl
All is ok when XML received start with:
<?xml version="1.0" encoding="UTF-8"?>
<p:FatturaElettronica xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2" versione="FPR12">
but I've received some XML starting with:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="fatturapa_v1.2.xsl"?>
<p:FatturaElettronica xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2" versione="FPR12">
in this case i get browser error when I try to open file because on my webapp i have not the fatturapa_v1.2.xml saved:
Error loading style sheet: XSLT style sheet interpretation failed.
Is there a way to strip out from this XML this line only, using PHP? Thanks
<?xml-stylesheet type="text/xsl" href="fatturapa_v1.2.xsl"?>
Everything in DOM is a node. In this case this is a processing instruction. You can use Xpath to find it and then remove it from its parent node:
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="fatturapa_v1.2.xsl"?>
<p:FatturaElettronica xmlns:p="http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2" versione="FPR12"/>
XML;
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('/processing-instruction()[name() = "xml-stylesheet"]') as $pi) {
// var_dump($pi);
$pi->parentNode->removeChild($pi);
}
echo $document->saveXML();

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;

SimpleXMLElement creating two XML Tags

I'm building xml with the SimpleXMLElement Object from PHP.
While doing so I encountered the following problem, which i can't solve:
I'm generating the root xml element like this:
$xml = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
But i get a 2 XML headers when i do echo $xml->asXML(); like this:
<?xml version="1.0"?>
<xml version="1.0" encoding="UTF-8"></xml>
Which is obvioulsy wrong. But how can i fix this so i only get the
<xml version="1.0" encoding="UTF-8">
part?
You must also supply the surrounding tag.
For example:
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?> <BASETAG />');

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