i currently have 2 XML files exactly the same structure, i want to remove the entrys (or nodes) in XMLfile1 if they exist in XMLfile2. The structure i am using is below :
XMLFile1:
<ArrivingFlights>
<flight>
<to>Ciara</to>
<from>Vikki xx</from>
<imagepath>0003.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>11:00</time>
<date>19/12/15</date>
</flight>
<flight>
<to>Eadaoin</to>
<from>Dylan xx</from>
<imagepath>0005.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>18:00</time>
<date>22/12/15</date>
</flight>
<flight>
<to>Michelle</to>
<from>Brianna xx</from>
<imagepath>0001.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>17:00</time>
<date>18/12/15</date>
</flight>
<flight>
<to>Ger</to>
<from>Mammy xx</from>
<imagepath>0002.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>08:00</time>
<date>21/12/15</date>
</flight>
</ArrivingFlights>
XMLFile2 :
<flight>
<to>Eadaoin</to>
<from>Dylan xx</from>
<imagepath>0005.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>18:00</time>
<date>22/12/15</date>
</flight>
In this example i would want to re-save file1 without the entry that you see in file2.
Thanks for your time.
Consider using XSLT, the special purpose language designed natively to restructure XML files. You can even reference external XML files using its document() function. Here XSL is loaded externally:
XSLT Script (save as .xsl or .xslt file)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Matches text between files and writes empty template (i.e. removes entire node) -->
<xsl:template match="flight[. = document('OtherFile.xml')/flight]"/>
</xsl:transform>
PHP Script
// Load the XML source and XSLT file
$doc = new DOMDocument();
$doc->load('Input.xml');
$xsl = new DOMDocument;
$xsl->load('XSLTScript.xsl');
// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// Transform XML source
$newXml = $proc->transformToXML($doc);
// Save output to file
$xmlfile = 'Output.xml';
file_put_contents($xmlfile, $newXml);
Output
<?xml version="1.0" encoding="UTF-8"?>
<ArrivingFlights>
<flight>
<to>Ciara</to>
<from>Vikki xx</from>
<imagepath>0003.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>11:00</time>
<date>19/12/15</date>
</flight>
<flight>
<to>Michelle</to>
<from>Brianna xx</from>
<imagepath>0001.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>17:00</time>
<date>18/12/15</date>
</flight>
<flight>
<to>Ger</to>
<from>Mammy xx</from>
<imagepath>0002.jpg</imagepath>
<templateStyle>template1</templateStyle>
<time>08:00</time>
<date>21/12/15</date>
</flight>
</ArrivingFlights>
you can also do it with Xpath query
$message = null;
$oldXml = new DOMDocument;
$oldXml->load('xm11.xml');
$option = array();
foreach ($oldXml->getElementsByTagName('flight') as $product) {
$option[] = '( to = "' . $product->getElementsByTagName('to')->item(0)->nodeValue . '" and from ="' . $product->getElementsByTagName('from')->item(0)->nodeValue . '")';//ArrivingFlights/flight[( to = "Ciara" and from ="Vikki")] your query format
}
$option = implode('or', $option);
$newXml = new DOMDocument;
$newXml->load('xml2.xml');
$xp = new DOMXPath($newXml);
$query1 = '/ArrivingFlights/flight[' . $option . ']';
echo $query1;
foreach ($xp->query($query1) as $product) {
echo $newXml->saveXML($product);//delete it
}
Related
I am parsing an XML file (source.xml) in PHP and need to identify instances where the <property> node contains the <rent> element.
Once identified the entire <property> parent node for that entry should be copied to a separate XML file (destination.xml).
On completion of the copy that <property> node should be removed from the source.xml file.
Here is an example of the source.xml file:
<?xml version="1.0" encoding="utf-8"?>
<root>
<property>
...
<rent>
<term>long</term>
<freq>month</freq>
<price_peak>1234</price_peak>
<price_high>1234</price_high>
<price_medium>1234</price_medium>
<price_low>1234</price_low>
</rent>
...
</property>
</root>
I've tried using DOM with the below code however I'm not getting any results at all despite their being hundreds of nodes that match the above requisites. Here is what I have so far:
$destination = new DOMDocument;
$destination->preserveWhiteSpace = true;
$destination->load('destination.xml');
$source = new DOMDocument;
$source->load('source.xml');
$xp = new DOMXPath($source);
foreach ($xp->query('/root/property/rent[term/freq/price_peak/price_high/price_medium/price_low]') as $item) {
$newItem = $destination->documentElement->appendChild(
$destination->createElement('property')
);
foreach (array('term', 'freq', 'price_peak', 'price_high', 'price_medium', 'price_low') as $elementName) {
$newItem->appendChild(
$destination->importNode(
$item->getElementsByTagName($elementName)->property(0),
true
)
);
}
}
$destination->formatOutput = true;
echo $destination->saveXml();
I've only started learning about DOMDocument and it's uses so I'm obviously messing up somewhere so any help is appreciated. Many thanks.
The difficulty is when your trying to copy a node from one document to another. You can try and re-create the node, copying all of the components across, but this is hard work (and prone to errors). Instead you can import the node from one document to another using importNode. The second parameter says copy all child elements as well.
Then deleting the element from the original document is a case of getting the item to 'delete itself from it's parent' which sounds odd, but thats how this code works.
<?php
error_reporting ( E_ALL );
ini_set ( 'display_errors', 1 );
$destination = new DOMDocument;
$destination->preserveWhiteSpace = true;
$destination->loadXML('<?xml version="1.0" encoding="utf-8"?><root></root>');
$source = new DOMDocument;
$source->load('NewFile.xml');
$xp = new DOMXPath($source);
$destRoot = $destination->getElementsByTagName("root")->item(0);
foreach ($xp->query('/root/property[rent]') as $item) {
$newItem = $destination->importNode($item, true);
$destRoot->appendChild($newItem);
$item->parentNode->removeChild($item);
}
echo "Source:".$source->saveXML();
$destination->formatOutput = true;
echo "destination:".$destination->saveXml();
With the destination, I prime it with the basic <root> element and then add in the contents from there.
Did you wanted to obtain something like this? Hope this helps:
$inXmlFile = getcwd() . "/source.xml";
$inXmlString = file_get_contents($inXmlFile);
$outXmlFile = getcwd() . "/destination.xml";
$outXmlString = file_get_contents($outXmlFile);
$sourceDOMDocument = new DOMDocument;
$sourceDOMDocument->loadXML($inXmlString);
$sourceRoot = null;
foreach ($sourceDOMDocument->childNodes as $childNode) {
if(strcmp($childNode->nodeName, "root") == 0) {
$sourceRoot = $childNode;
break;
}
}
$destDOMDocument = new DOMDocument;
$destDOMDocument->loadXML($outXmlString);
$destRoot = null;
foreach ($destDOMDocument->childNodes as $childNode) {
if(strcmp($childNode->nodeName, "root") == 0) {
$destRoot = $childNode;
break;
}
}
$xmlStructure = simplexml_load_string($inXmlString);
$domProperty = dom_import_simplexml($xmlStructure->property);
$rents = $domProperty->getElementsByTagName('rent');
if(($rents != null) && (count($rents) > 0)) {
$destRoot->appendChild($destDOMDocument->importNode($domProperty->cloneNode(true), true));
$destDOMDocument->save($outXmlFile);
$sourceRoot->removeChild($sourceRoot->getElementsByTagName('property')->item(0));
$sourceDOMDocument->save($inXmlFile);
}
Consider running two XSLT transformations: one that adds <property><rent> nodes in destination and one that removes these nodes from source. As background, XSLT is a special-purpose language designed to transform XML files even maintaining a document() function to parse from external XML files in same folder or subfolder.
PHP can run XSLT 1.0 scripts with its php-xsl class (be sure to enable extension in .ini file). With this approach no if logic or foreach loops are needed.
XSLT Scripts
PropertyRentAdd.xsl (be sure source.xml and XSLT are in same folder)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- ADD TEMPLATE -->
<xsl:template match="root">
<xsl:copy>
<xsl:copy-of select="*"/>
<xsl:copy-of select="document('source.xml')/root/property[local-name(*)='rent']"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
PropertyRentRemove.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- IDENTITY TRANSFORM -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- REMOVE TEMPLATE -->
<xsl:template match="property[local-name(*)='rent']">
</xsl:template>
</xsl:stylesheet>
PHP
// Set current path
$cd = dirname(__FILE__);
// Load the XML and XSLT files
$doc = new DOMDocument();
$doc->load($cd.'/destination.xml');
$xsl = new DOMDocument;
$xsl->load($cd.'/PropertRentAdd.xsl');
// Transform the destination xml
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
$newXml = $proc->transformToXML($xml);
// Save output to file, overwriting original
file_put_contents($cd.'/destination.xml', $newXml);
// Load the XML and XSLT files
$doc = new DOMDocument();
$doc->load($cd.'/source.xml');
$xsl = new DOMDocument;
$xsl->load($cd.'/PropertRentRemove.xsl');
// Transform the source xml
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
$newXml = $proc->transformToXML($xml);
// Save output overwriting original file
file_put_contents($cd.'/source.xml', $newXml);
Inputs (examples to demonstrate, with other tags to show content is not affected)
source.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
<property>
<rent>
<term>long</term>
<freq>month</freq>
<price_peak>1234</price_peak>
<price_high>1234</price_high>
<price_medium>1234</price_medium>
<price_low>1234</price_low>
</rent>
</property>
<property>
<rent>
<term>short</term>
<freq>month</freq>
<price_peak>7890</price_peak>
<price_high>7890</price_high>
<price_medium>7890</price_medium>
<price_low>7890</price_low>
</rent>
</property>
<property>
<web_site>stackoverflow</web_site>
<general_purpose>php</general_purpose>
</property>
<property>
<web_site>stackoverflow</web_site>
<special_purpose>xsl</special_purpose>
</property>
</root>
destination.xml
<?xml version="1.0" encoding="utf-8"?>
<root>
<original_data>
<test1>ABC</test1>
<test2>123</test2>
</original_data>
<original_data>
<test1>XYZ</test1>
<test2>789</test2>
</original_data>
</root>
Output (after PHP run)
source.xml
<?xml version="1.0"?>
<root>
<property>
<web_site>stackoverflow</web_site>
<general_purpose>php</general_purpose>
</property>
<property>
<web_site>stackoverflow</web_site>
<special_purpose>xsl</special_purpose>
</property>
</root>
destination.xml (new nodes appended at bottom)
<?xml version="1.0"?>
<root>
<original_data>
<test1>ABC</test1>
<test2>123</test2>
</original_data>
<original_data>
<test1>XYZ</test1>
<test2>789</test2>
</original_data>
<property>
<rent>
<term>long</term>
<freq>month</freq>
<price_peak>1234</price_peak>
<price_high>1234</price_high>
<price_medium>1234</price_medium>
<price_low>1234</price_low>
</rent>
</property>
<property>
<rent>
<term>short</term>
<freq>month</freq>
<price_peak>7890</price_peak>
<price_high>7890</price_high>
<price_medium>7890</price_medium>
<price_low>7890</price_low>
</rent>
</property>
</root>
I have a problem... I want to rename the tags in some XML files. An it works with this code:
$xml = file_get_contents('data/onlinekeystore.xml');
renameTags($xml, 'priceEUR', 'price', 'data/onlinekeystore.xml');
But if I want to rename another XML file it doens't work with the SAME method...
See the example below. I have no idea why...
Does anybody has an idea and can help me?
$xml = file_get_contents('data/g2a.xml');
renameTags($xml, 'name', 'title', 'data/g2a.xml');
Function Code:
function renameTags($xml, $old, $new, $path){
$dom = new DOMDocument();
$dom->loadXML($xml);
$nodes = $dom->getElementsByTagName($old);
$toRemove = array();
foreach ($nodes as $node) {
$newNode = $dom->createElement($new);
foreach ($node->attributes as $attribute) {
$newNode->setAttribute($attribute->name, $attribute->value);
}
foreach ($node->childNodes as $child) {
$newNode->appendChild($node->removeChild($child));
}
$node->parentNode->appendChild($newNode);
$toRemove[] = $node;
}
foreach ($toRemove as $node) {
$node->parentNode->removeChild($node);
}
$dom->saveXML();
$dom->save($path);
}
onlinekeystore.xml Input:
<product>
<priceEUR>5.95</priceEUR>
</product>
onlinekeystore.xml Ouput:
<product>
<price>5.95</price>
</product>
g2a.xml Input:
<products>
<name><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></name>
</products>
g2a.xml Ouput:
<products>
<name><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></name>
</products>
Greetings
Consider a dynamic XSLT running the Identity Transform and then updates node names in a specific template anywhere in document. The sprintf formats XSL string passing in $old and $new values. This process avoids any nested looping through entire tree and even pretty prints output no matter the format of input.
function renameTags($xml, $old, $new, $path){
// LOAD XML
$dom = new DOMDocument();
$dom->loadXML($xml);
// LOAD XSL
$xslstr = '<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml" cdata-section-elements="%2$s"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="%1$s">
<xsl:element name="%2$s">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:transform>';
$xsl = new DOMDocument();
$xsl->loadXML(sprintf($xslstr, $old, $new));
// INITIALIZE TRANSFORMER (REQUIRES php_xsl EXTENSION ENABLED IN .ini)
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// TRANSFORM XML AND SAVE OUTPUT
$newXML = $proc->transformToXML($dom);
file_put_contents($path, $newXML);
}
Output
renameTags('<product><priceEUR>5.95</priceEUR></product>', 'priceEUR', 'price', 'data/onlinekeystore.xml');
// <?xml version="1.0" encoding="UTF-8"?>
// <product>
// <price><![CDATA[5.95]]></price>
// </product>
renameTags('<products><name><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></name></products>', 'name', 'title', 'data/g2a.xml');
// <?xml version="1.0" encoding="UTF-8"?>
// <products>
// <title><![CDATA[1 Random STEAM PREMIUM CD-KEY]]></title>
// </products>
Note: In XSLT, the <![CDATA[...]] tags are not preserved unless explicitly specified in <xsl:output>. Right now, any new node's text is wrapped with it. Remove the output spec and no CData tags render. So either include such escape tags for all or none.
I have an xml file which needs to be formatted in a specific way to ensure readability.
Source XML
<?xml version="1.0" encoding="UTF-8"?>
<facility>
<attributes>
<id>1</id>
<name>Facility Name</name>
<coordinate>Lat,Long</coordinate>
<projection>mercator</projection>
<units>imperial</units>
<showcart>yes</showcart>
<shotplanner>yes</shotplanner>
<sound>on</sound>
<gfbkgcolor>#ff0000</gfbkgcolor>
<gftxtcolor>#ffffff</gftxtcolor>
<gcbkgcolor>#ffffff</gcbkgcolor>
<gctxtcolor>#000000</gctxtcolor>
</attributes>
</facility>
Expected Output
<facility name="Facility Name" id="1"
coordinate="Lat,Long" projection="mercator"
units="imperial" showcart="yes" shotplanner="yes"
sound="on" gfbkgcolor="#ff0000" gftxtcolor="#ffffff"
gcbkgcolor="#ffffff">
</facility>
Ive got a complex xml file but thats pretty much what i'm trying to do.
XSL template
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="facility/attributes">
<facility id="{id}" name="{name}"
coordinate="{coordinate}">
</facility>
<xsl:text>
</xsl:text>
<test>"hello"</test>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Using this <xsl:text>
</xsl:text> seems to be throwing an error and does not display the output xml.
Is it possible to have line breaks after certain attributes using xsl?
Can i have line breaks between children in a given element ( So they could be grouped logically )?
I'm forming the above xml via php. Once i have the xml, i'd like it to be formatted as mentioned above. I can do it using php strings but its a huge file and would be quite tedious to do so. Can xsl be used to solve this problem?
Any pointers on how to achieve this would be much appreciated. Thx
I don't know of any XML serializer (XSLT-based or otherwise) that gives you that level of control over the formatting of a set of attributes, though you could probably get close with Saxon by taking advantage of the saxon:attribute-order and saxon:line-length properties on xsl:output: see http://www.saxonica.com/documentation/index.html#!extensions/output-extras/serialization-parameters .
It's not clear why your current code is throwing an error, but since you haven't told us what the error is, I won't try to answer that part of the question.
Being PHP is a general purpose language and equipped with an XSLT 1.0 processor, you can handle both operations, xml transformation and text formatting, in one script:
XSLT Script (save externally to be loaded below or add as an embedded string with loadXML())
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="attributes/*"/>
</xsl:copy>
</xsl:template>
<!-- Migrate Nodes to Attributes -->
<xsl:template match="attributes/*">
<xsl:attribute name="{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:transform>
PHP Script
<?php
// Load the XML source and XSLT file
$doc = new DOMDocument();
$doc->load('Source.xml');
$xsl = new DOMDocument;
$xsl->load('XSLTScript.xsl');
// Configure the processor
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// Transform XML source
$newXml = $proc->transformToXML($doc);
// Add line breaks, tabs, and spaces between attributes to output string
$newXml = str_replace("coordinate", "\n\t\t coordinate", $newXml);
$newXml = str_replace("units", "\n\t\t units", $newXml);
$newXml = str_replace("sound", "\n\t\t sound", $newXml);
$newXml = str_replace("gcbkgcolor", "\n\t\t gcbkgcolor", $newXml);
$newXml = str_replace("/>", ">\n</facility>", $newXml);
echo $newXml;
// Save output to file
$xmlfile = 'Output.xml';
file_put_contents($xmlfile, $newXml);
?>
Output
<?xml version="1.0" encoding="UTF-8"?>
<facility id="1" name="Facility Name"
coordinate="Lat,Long" projection="mercator"
units="imperial" showcart="yes" shotplanner="yes"
sound="on" gfbkgcolor="#ff0000" gftxtcolor="#ffffff"
gcbkgcolor="#ffffff" gctxtcolor="#000000">
</facility>
<?php
public function writeToXml(){
$data = array(
"id" => 15,
"name" => "Facility Name",
"coordinate" => "Lat,lon",
"projection" => "mercator",
"units" => "imperial",
"showcart" => "yes",
"shotplanner" => "yes",
"sound" => "on",
"gfbkgcolor" => "#ff0000",
"gftxtcolor" => "#ffffff",
"gcbkgcolor" => "#ffffff",
"gctxtcolor" => "#000000",
"gbbkgcolor" => "#0000ff",
"gbtxtcolor" => "#ffffff"
);
// Inserts a new line with a specified indent
function insertNewLine($indent = 0){
$line = "\n";
$line .= str_repeat("\t", $indent);
return $line;
}
// Formats a given string with a trailing space
function formatString($string) {
$data = "\"" . $string . "\" ";
return $data;
}
$facility = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
$facility .= insertNewLine();
$facility .= "<facility name=" . formatString($data['name']);
$facility .= "coordinate=" . formatString($data["coordinate"]);
$facility .= insertNewLine(1);
$facility .= "projection=" . formatString($data['projection']);
$facility .= "units=" . formatString($data['units']);
$facility .= "showcart=" . formatString($data['showcart']);
$facility .= "shotplanner=" . formatString($data['shotplanner']);
$facility .= "sound=" . formatString($data['sound']);
$facility .= insertNewLine(1);
$facility .= "gfbkgcolor=" . formatString($data['gfbkgcolor']);
$facility .= "gftxtcolor=" . formatString($data['gftxtcolor']);
$facility .= insertNewLine(1);
$facility .= "gcbkgcolor=" . formatString($data['gcbkgcolor']);
$facility .= "gctxtcolor=" . formatString($data['gctxtcolor']);
$facility .= "/>";
$myfile = fopen('application/packages/myfile.txt', 'w+');
fwrite($myfile, $facility);
fclose($myfile);
// echo $facility;
});
I am having an issue with a recursive function I am trying to work on. I have an XML file that essentially documents a file/folder structure. These XML nodes (which represent a file/folder) can be N levels deeps. So, I am trying to write a script that iterates over all the nodes and creates entries into a database. For the folders, I have a table structure that has an ID, field_name, and parent_id columns. The parent_id points to the ID of a folder the current folder resides in. If it's at the root level, the ID is 0.
My problem is that I'm not able to accurately keep track of the parent_id, when I get down lover levels, then comes back up. Here is an example of XML, but realize the folders can be any number of levels:
<XML>
<programs>
<program name ="xxx">
<groups>
<group id ="1" name ="yyy">
<folder name = "ggg">
<file name = "ddfdf"/>
<file name = "ddfdf"/>
<folder name = "sub" />
</folder>
<folder name = "sdfsdfs">
<file name = "ddfdf"/>
<folder name = "sub" >
<file name = "ddfdf"/>
</folder>
</folder>
</group>
</groups>
</program>
</programs>
</xml>
Script:
foreach($program as $p){
//creates root folder and returns ID
$id = create_folder($folder);
$rootId = $id;
$groups = $program->groups;
if($p->groups){
foreach($p->groups as $group){
foreach($group as $folder){
process_folder($folder,$id, $rootId);
}
}
}
}
function process_folder(($folder,$id, $rootId){
foreach($folder as $key=>$value){
switch ($key){
case "folder":
//creates folder, then returns the ID of the db record
$parentId = create_folder($folder);
process_folder($value, $parentId, $rootId);
//reset ID but this doesnt seem to work
$parentId = $rootId;
break;
case "file":
break;
}
}
}
This is not a complete solution but it shows how a recursiveIterator might be used to accomplish what you want to do.
$strxml='
<xml>
<programs>
<program name ="xxx">
<groups>
<group id ="1" name ="yyy">
<folder name = "ggg">
<file name = "ddfdf"/>
<file name = "ddfdf"/>
<folder name = "sub" />
</folder>
<folder name = "sdfsdfs">
<file name = "ddfdf"/>
<folder name = "sub" >
<file name = "ddfdf"/>
</folder>
</folder>
</group>
</groups>
</program>
</programs>
</xml>';
class RecursiveDOMIterator implements RecursiveIterator {
private $index;
private $list;
public function __construct(DOMNode $domNode){
$this->index = 0;
$this->list = $domNode->childNodes;
}
public function current(){
return $this->list->item($this->index);
}
public function getChildren(){
return new self( $this->current() );
}
public function hasChildren(){
return $this->current()->hasChildNodes();
}
public function key(){
return $this->index;
}
public function next(){
$this->index++;
}
public function rewind(){
$this->index = 0;
}
public function valid(){
return $this->index < $this->list->length;
}
}
$xml=new DOMDocument;
$xml->loadXML( $strxml );
$rootnode=$xml->getElementsByTagName('programs')->item(0);
$nodeItr=new RecursiveDOMIterator( $rootnode );
$itr=new RecursiveIteratorIterator( $nodeItr, RecursiveIteratorIterator::SELF_FIRST );
foreach( $itr as $node ) {
if( $node->nodeType === XML_ELEMENT_NODE ) {
$id=$node->hasAttribute('id') ? $node->getAttribute('id') : false;
$attr=$node->hasAttribute('name') ? $node->getAttribute('name') : false;
echo $id.' '.$node->nodeName . ' ' . $node->nodeValue. ' ' . $attr .' '.$node->parentNode->tagName . ' ' . '<br />';
}
}
$dom=$rootnode=$itr=$nodeItr=null;
Consider using XSLT to simplify your XML to a one child level format. Then run a simple PHP loop to extract parent id and folder data for database migration. In fact, if using MySQL you can import this exact transformed XML document into database with LoadXML(), should node names match column names.
As information, XSLT is a special-purpose, declarative programming language (same type as SQL) used to re-structure XML documents to various structures for end use needs. Like all general purpose languages including C#, Java, Python, Perl, VB, PHP maintains an XSLT processor. Below is an XSLT script (which you can add to for each nest in XML document) and PHP script (to transform and iterate through output).
XSLT Script (save as .xsl or .xslt)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="*"/>
</root>
</xsl:template>
<xsl:template match="folder" name="foldertemplate">
<row>
<parent_id>0</parent_id>
<field_name><xsl:value-of select="#name"/></field_name>
</row>
<!-- ADD LEVELS FOR EACH NEST IN XML DOCUMENT -->
<row>
<parent_id><xsl:value-of select="#name"/></parent_id>
<field_name><xsl:value-of select="folder/#name"/></field_name>
</row>
<!-- EXAMPLE NEXT LEVEL -->
<!-- <row> -->
<!-- <parent_id><xsl:value-of select="folder/#name"/></parent_id> -->
<!-- <field_name><xsl:value-of select="folder/folder/#name"/></field_name> -->
<!-- </row> -->
</xsl:template>
</xsl:transform>
TRANSFORMED XML OUTPUT (easier to parse and iterate over)
<?xml version="1.0" encoding="UTF-8"?>
<root>
<row>
<parent_id>0</parent_id>
<field_name>ggg</field_name>
</row>
<row>
<parent_id>ggg</parent_id>
<field_name>sub</field_name>
</row>
<row>
<parent_id>0</parent_id>
<field_name>sdfsdfs</field_name>
</row>
<row>
<parent_id>sdfsdfs</parent_id>
<field_name>sub</field_name>
</row>
</root>
PHP Script (to transform and loop through output)
// Set current directory
$cd = dirname(__FILE__);
// Load the XML source and XSLT file
$doc = new DOMDocument();
$doc->load($cd.'/Input.xml');
$xsl = new DOMDocument;
$xsl->load($cd.'/XSLTScript.xsl');
// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// Transform XML source
$newXml = $proc->transformToXML($doc);
// Save output to file
$xmlfile = $cd.'/Output.xml';
file_put_contents($xmlfile, $newXml);
// Load new XML with SimpleXML
$newdoc = simplexml_load_file($cd.'/Output.xml');
$data = [];
$node = $newdoc->xpath('//row');
$parents = $newdoc->xpath('//row/parent_id');
$folders = $newdoc->xpath('//row/field_name');
// Loop through folder names and parent
for($i=0; $i < sizeof($node); $i++) {
echo 'parent: '.$parents[$i]. ' folder: ' . $folders[$i]."\n";
}
#parent: 0 folder: ggg
#parent: ggg folder: sub
#parent: 0 folder: sdfsdfs
#parent: sdfsdfs folder: sub
I have a PHP script that caches a remote XML file. I want to XSL transform it before caching, but don't know how to do this:
<?php
// Set this to your link Id
$linkId = "0oiy8Plr697u3puyJy9VTUWfPrCEvEgJR";
// Set this to a directory that has write permissions
// for this script
$cacheDir = "temp/";
$cachetime = 15 * 60; // 15 minutes
// Do not change anything below this line
// unless you are absolutely sure
$feedUrl="http://mydomain.com/messageService/guestlinkservlet?glId=";
$cachefile = $cacheDir .$linkId.".xml";
header('Content-type: text/xml');
// Send from the cache if $cachetime is not exceeded
if (file_exists($cachefile) && (time() - $cachetime
< filemtime($cachefile)))
{
include($cachefile);
echo "<!-- Cached ".date('jS F Y H:i', filemtime($cachefile))." -->\n";
exit;
}
$contents = file_get_contents($feedUrl . $linkId);
// show the contents of the XML file
echo $contents;
// write it to the cache
$fp = fopen($cachefile, 'w');
fwrite($fp, $contents);
fclose($fp);
?>
This is the XSL string I want to use to transform it:
<xsl:template match="/">
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<xsl:apply-templates select="messageList" />
</Document>
</kml>
</xsl:template>
<xsl:template match="messageList">
<name>My Generated KML</name>
<xsl:apply-templates select="message" />
</xsl:template>
<xsl:template match="message">
<Placemark>
<name><xsl:value-of select="esnName" /></name>
<Point>
<coordinates>
<xsl:value-of select="latitude" />,<xsl:value-of select="longitude" />
</coordinates>
</Point>
</Placemark>
</xsl:template>
I want to actually transform XML input and save/return a KML format. Can someone please help adjust this script? This was given to me and I am a little new to it.
$domOrigin = new DOMDocument('1.0');
$domOrigin->loadXML($contents);
$domXsl = new DOMDocument('1.0');
$domXsl->load('/path/to/stylesheet.xsl',LIBXML_NOCDATA);
$processor = new XSLTProcessor();
$processor->importStylesheet($domXsl);
file_put_contents($cachfile, $processor->transformToXml($domOrigin));
Ill leave it to you to integrate that :-)
Use the XSL extension, there is an example here:
http://www.php.net/manual/en/book.xsl.php#90510
replace your file_get_contents call with:
$XML = new DOMDocument();
$XML->load( $feedUrl . $linkId );
/*
this is the same as the example:
*/
$xslt = new XSLTProcessor();
$XSL = new DOMDocument();
$XSL->load( $xslFile );
$xslt->importStylesheet( $XSL );
then replace the "print" line with file_put_contents( $cachefile, $xslt->transformToXML( $XML ) );
Never use include on anything except a local php source file. It will process the included contents as PHP source. Here is readfile() for other content.
ext/xslt provides an XSLT 1.0 processor with EXSLT support.
I suggest using the cache file as a fallback if here is an error fetch or transforming the external resource also.
$contents = '';
// validate cache
if (
(!$useCache) ||
(!file_exists($chacheFile)) ||
(filemtime($cacheFile) > time() - $expires)
) {
// fetch and transform xml content
$contents = fetchAndTransform($xmlFile, $xsltFile);
if ($useCache) {
// write cache file
file_put_contents($cacheFile, $contents);
}
}
// invalid cache or fetch/transform failure - read cache file
if ($useCache && !$contents) {
// read cache file
$contents = file_get_contents($cacheFile);
}
// output if here is contents
if ($contents) {
} else {
// error handling
}
function fetchAndTransform(string $xmlFile, string $xsltFile): string {
// read xml into DOM
$xmlDocument = new DOMDocument();
$xmlDocument->load($xmlFile);
// read xsl into DOM
$xslDocument = new DOMDocument();
$xslDocument->load($xsltFile);
// initialize xslt processor
$processor = new XSLTProcessor();
// import template
$processor->importStylesheet($xslDocument);
// transform
return $processor->transformToXml($xmlDocument);
}
The namespaces are applied to the nodes while parsing the XSLT (during $xslDocument->load($xsltFile). You need to define the KML namespace (http://www.opengis.net/kml/2.2) on the XSL stylesheet element. So that the parser will put any element without a namespace prefix into it. Otherwise Placemark, name, ... will not be in the KML namespace.
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.opengis.net/kml/2.2">
<xsl:template match="/">
<kml>
<Document>
<xsl:apply-templates select="messageList" />
</Document>
</kml>
</xsl:template>
<xsl:template match="messageList">
<name>My Generated KML</name>
<xsl:apply-templates select="message" />
</xsl:template>
<xsl:template match="message">
<Placemark>
<name><xsl:value-of select="esnName" /></name>
<Point>
<coordinates>
<xsl:value-of select="latitude" />,<xsl:value-of select="longitude" />
</coordinates>
</Point>
</Placemark>
</xsl:template>
</xsl:stylesheet>
XSLT;