I am using PHP curl to retrive an xml file from a remote url and save it to a local file on my server. The structure is the following:
<Store Country="Ireland">
<EventsPoints>
<Event ID="1800" >
<ArtistIDs>
<ArtistID ID="109" Type="Primary" />
</ArtistIDs>
<CategoryID>1</CategoryID>
<Country>IRL</Country>
<PerformanceName>Music and Arts</PerformanceName>
<VenueID ID="197" />
</Event>
<Venues>
<Venue ID="197">
<City>Dublin</City>
<Country>IRL</Country>
<VenueName>ABC</VenueName>
<VenueNumber>22</VenueNumber>
</Venue>
</Venues>
The above xml blocks are stored in the same XML file. There are several Event blocks and several Venue blocks.
The problem i'm having is using PHP to access this large XML file and iterate through the Venue blocks retrieving only a Venue block with a certain ID specified via a parameter.
I then want to iterate through the event blocks - only retrieving the events matching this specified venue ID. I then want to save this to a file on the server.
I want to do this for each venue.
How do I go about doing the above?
EDIT:
For each Venue and events related to that venue, I just want to literally copy them to their own file - basically splitting down the larger file into individual files
$docSource = new DOMDocument();
$docSource->loadXML($xml);
$docDest = new DOMDocument();
$docDest->loadXML(file_get_contents('/var/www/html/xml/testfile.xml'));
$xpath = new DOMXPath($docSource);
$id = "197";
$result = $xpath->query('//Event/VenueID[#ID=$id]')->item(0); //Get directly the node you want
$result = $docDest->importNode($result, true); //Copy the node to the other document
$items = $docDest->getElementsByTagName('items')->item(0);
$items->appendChild($result); //Add the copied node to the destination document
echo $docDest->saveXML();
You are not showing the desired output format, so I will assume this generates what you want. If not, feel free to modify the code so it meets the desired output format. This along with the comments above should have all you need to get this working on your own.
// load Source document
$srcDom = new DOMDocument;
$srcDom->load('/var/www/html/xml/testfile.xml');
$xPath = new DOMXPath($srcDom);
// iterate over all the venues in the source document
foreach ($srcDom->getElementsByTagName('Venue') as $venue) {
// create a new destination document for the current venue
$dstDom = new DOMDocument('1.0', 'utf-8');
// add an EventsPoint element as the root node
$dstDom->appendChild($dstDom->createElement('EventsPoint'));
// import the Venue element tree to the new destination document
$dstDom->documentElement->appendChild($dstDom->importNode($venue, true));
// fetch all the events for the current venue from the source document
$allEventsForVenue = $xPath->query(
sprintf(
'/Store/EventsPoints/Event[VenueID/#ID=%d]',
$venue->getAttribute('ID')
)
);
// iterate all the events found in Xpath query
foreach ($allEventsForVenue as $event) {
// add event element tree to current destination document
$dstDom->documentElement->appendChild($dstDom->importNode($event, true));
}
// make output prettier
$dstDom->formatOutput = true;
// save XML to file named after venue ID
$dstDom->save(sprintf('/path/to/%d.xml', $venue->getAttribute('ID')));
}
This will create an XML file like this
<?xml version="1.0" encoding="utf-8"?>
<EventsPoint>
<Venue ID="197">
<City>Dublin</City>
<Country>IRL</Country>
<VenueName>ABC</VenueName>
<VenueNumber>22</VenueNumber>
</Venue>
<Event ID="1800">
<ArtistIDs>
<ArtistID ID="109" Type="Primary"/>
</ArtistIDs>
<CategoryID>1</CategoryID>
<Country>IRL</Country>
<PerformanceName>Music and Arts</PerformanceName>
<VenueID ID="197"/>
</Event>
</EventsPoint>
in the file 197.xml
Related
I just wanted to ask how I can insert a new node in an XML using PHP. my XML file (questions.xml) is given below
<?xml version="1.0" encoding="UTF-8"?>
<Quiz>
<topic text="Preparation for Exam">
<subtopic text="Science" />
<subtopic text="Maths" />
<subtopic text="english" />
</topic>
</Quiz>
I want to add a new "subtopic" with the "text" attribute, that is "geography". How can I do this using PHP? Thanks in advance though.
well my code is
<?php
$xmldoc = new DOMDocument();
$xmldoc->load('questions.xml');
$root = $xmldoc->firstChild;
$newElement = $xmldoc->createElement('subtopic');
$root->appendChild($newElement);
// $newText = $xmldoc->createTextNode('geology');
// $newElement->appendChild($newText);
$xmldoc->save('questions.xml');
?>
I'd use SimpleXML for this. It would look somehow like this:
// Open and parse the XML file
$xml = simplexml_load_file("questions.xml");
// Create a child in the first topic node
$child = $xml->topic[0]->addChild("subtopic");
// Add the text attribute
$child->addAttribute("text", "geography");
You can either display the new XML code with echo or store it in a file.
// Display the new XML code
echo $xml->asXML();
// Store new XML code in questions.xml
$xml->asXML("questions.xml");
The best and safe way is to load your XML document into a PHP DOMDocument object, and then go to your desired node, add a child, and finally save the new version of the XML into a file.
Take a look at the documentation : DOMDocument
Example of code:
// open and load a XML file
$dom = new DomDocument();
$dom->load('your_file.xml');
// Apply some modification
$specificNode = $dom->getElementsByTagName('node_to_catch');
$newSubTopic = $xmldoc->createElement('subtopic');
$newSubTopicText = $xmldoc->createTextNode('geography');
$newSubTopic->appendChild($newSubTopicText);
$specificNode->appendChild($newSubTopic);
// Save the new version of the file
$dom->save('your_file_v2.xml');
You can use PHP's Simple XML. You have to read the file content, add the node with Simple XML and write the content back.
My project is to use CKEditor and create a plugin to create chart inside the page which will display the result
(in CKEditor just an img will appear when you use the plugin).
The plugin is already created,
when you use it, XML with the value and attribute needed is created inside CKEditor.
So the XML for the chart exist with writing text (format as <p> by CKEditor).
When you save inside the CKEditor, the XML and the text as HTML are sent to the database inside a string
(when I retrieve data from CKEditor it send a string).
Inside the database it is stored like this:
<?xml version="1.0"?>
<configuration>
<onglet pos="1" name="test">
<wysiwyg>
<code>
<p>test</p>
<p><graphique drawer="Line" interval="2" periode="semaine" titre="test" type="Relative">
<serie marqueur="Normal">
<index serial="---" type="di">3</index>
<typegroupe></typegroupe>
<intervalgroup></intervalgroup>
<periodgroup></periodgroup>
<groupverif>non</groupverif>
<unite>Kwh</unite>
<legend>test2</legend>
<color>#000000</color>
</serie>
</graphique></p>
</code>
</wysiwyg>
</onglet>
</configuration>
I get it inside a php page to displays my text and chart from CKEditor
Like this:
$test=200; //ID
$testxml=loadxml($test);
$testonglet=$testxml->onglet;
$testwysiwyg=$testonglet->wysiwyg;
$testcode=$testwysiwyg->code;
loadxml is a php function in another file:
$loadXml = THE QUERY TO RETRIEVE THE XML FROM THE DB
$reqload = mysql_fetch_array(mysql_query($loadXml));
$decode = html_entity_decode($reqload[0]);
$xml = simplexml_load_string($decode);
return $xml;
Now if I do var_dump($testcode);
I got:
object(SimpleXMLElement)#6 (1) { [0]=> string(403) "
test
4nonKwhtest2#000000
test après graph
" }
test and test après graph are just text but the 4nonKwhtest2#000000 are the XML value from the plugin chart.
I would like to parse the string to retrive the text and the XML (with his structure since I need it to create the chart with php).
I have already tested:
$testXML = Simplexml_load_string($testcode);
var_dump($testXML);
It gave me:
bool(false)
I have the same from XMLReader.
I also tested:
$dom = new DOMDocument();
$dom->loadHTML($testcode);
It gave something with the text but I couldn't retrieve my XML from it.
I don't know how to handle this anymore, it's the first time I use PHP
I hope it's more clear like this (sorry for my english).
Basically you have an XML document that contains another XML fragment as a text node.
That means you need to load the outer XML document first, read the text node and append the XML fragment to a new XML document.
Load The Outer XML / Read The Fragment:
DOMXpath::evaluate() allows to fetch nodes and values from a XML DOM using XPath expression.
$outer = new DOMDocument();
$outer->loadXml($xml);
$xpath = new DOMXPath($outer);
$innerXml = $xpath->evaluate('string(/configuration/onglet/wysiwyg/code)');
echo $innerXml;
Output:
<p>test</p>
<p><graphique drawer="Line" interval="2" periode="semaine" titre="test" type="Relative">
<serie marqueur="Normal">
<index serial="---" type="di">3</index>
<typegroupe></typegroupe>
<intervalgroup></intervalgroup>
<periodgroup></periodgroup>
<groupverif>non</groupverif>
<unite>Kwh</unite>
<legend>test2</legend>
<color>#000000</color>
</serie>
</graphique></p>
Load The XML Fragment
XML documents have a single root/document element node, so the XML in the case is a fragment. For easy reading it needs the single root node. So lets create a document with a body node and append the fragment to it:
$inner = new DOMDocument();
$inner->appendChild($inner->createElement('body'));
$fragment = $inner->createDocumentFragment();
$fragment->appendXml($innerXml);
$inner->documentElement->appendChild($fragment);
echo $inner->saveXml();
Output:
<?xml version="1.0"?>
<body>
<p>test</p>
<p><graphique drawer="Line" interval="2" periode="semaine" titre="test" type="Relative">
<serie marqueur="Normal">
<index serial="---" type="di">3</index>
<typegroupe></typegroupe>
<intervalgroup></intervalgroup>
<periodgroup></periodgroup>
<groupverif>non</groupverif>
<unite>Kwh</unite>
<legend>test2</legend>
<color>#000000</color>
</serie>
</graphique></p>
</body>
The element name doesn't matter, but body fits the p elements.
Read Data From The New Document
Now it is possible to create an DOMXPath instance for the document and fetch data from it.
$xpath = new DOMXPath($inner);
foreach ($xpath->evaluate('//graphique') as $graphique) {
var_dump(
[
'drawer' => $graphique->getAttribute('drawer'),
'serie-color' => $xpath->evaluate('string(serie/color)', $graphique)
]
);
}
Output:
array(2) {
["drawer"]=>
string(4) "Line"
["serie-color"]=>
string(7) "#000000"
}
Ok, please bear with me as I explain this app. The first view shows a tableview, where each row is a 'request' that someone has made, also using the app. The requests are actually a parsed xml file that looks something like this:
<item>
<title>Request</title>
<name>Name</name>
<Responses>0</Responses>
</item>
Using the script listed underneath this paragraph, I am able to run some code in iOS that will take text from text-boxes in the app, and append the XML with a new item set.
<?php
$firstName = $_POST['firstName'];
$lastName = $_POST['lastName'];
$request = $_POST['request'];
$anon = $_POST['anon'];
$pubDate = $_POST['pubDate'];
$loc = $_POST['loc'];
//This line will load the XML file
$xml = simplexml_load_file("URL.xml") or die("Not loaded!\n");
//print_r($xml);
//This line gets the channel element (from an array returned by the xpath method)
$channel = $xml->xpath('//channel');
$channel = $channel[0];
//print_r($channel);
$person = $channel->addChild("item");
$person->addChild("first_name", $firstName);
$person->addChild("last_name", $lastName);
$person->addChild("title", $request);
$person->addChild("date", $pubDate);
$person->addChild("anonymous", $anon);
$person->addChild("responses", "0");
$person->addChild("location", $loc);
//This next line will overwrite the original XML file with new data added
$xml->asXML("Test.xml");
?>
The actual XML has much more listed than what I put at the beginning, I just edited it down to save space, so that's why it and the PHP don't quite match up.
All of this works great, the app parses the XML to show what has been requested, and a user can make a new request that will then show in the app.
My problem is that when someone responds to an existing request, I would like the <Responses> to increase by 1. Is there something I can do with a different script that would find that entry, and +1 to whatever number currently exists in that item's Response tag?
I have seen this script, but it appears to go through and search for all of a certain tag, and change all tags of that name in the XML. I am looking to just find one specific one and change it. Suggestions?
$str = <<<XML
<ScrapBook>
<Event>
<Name> Name of Event </Name>
<Blah>glop glop</Blah>
</Event>
</ScrapBook>
XML;
$xmlDoc = new DOMDocument();
$xmlDoc->loadXml($str);
$events = $xmlDoc->getElementsByTagName("Event");
foreach($events as $event){
$eventNames = $event->getElementsByTagName("Name");
$eventN = $eventNames->item(0)->nodeValue;
if(' Name of Event ' == $eventN){
$eventNames->item(0)->nodeValue = 'New name';
}
}
var_dump($xmlDoc->saveXML());
You can go with simplexml to do that; first, select the <item> to be changed, then store the new <Responses>:
$xml = simplexml_load_string($x); // assume XML in $x
// select <Responses> with xpath by title and name
$responses = $xml->xpath("/items/item[title = 'Foo' and name = 'Bar']/Responses");
// store number + 1
$responses[0][0] = $responses[0] + 1;
see it working: https://eval.in/127882
I'm trying to get the data from an XML file into an array so that I can import it via 'Magmi'. Using the following code, I'm working with a 3.6GB XML file.
<?php
$z = new XMLReader;
$z->open('wpcatsub.xml');
$doc = new DOMDocument;
// move to the first <App /> node
while ($z->read() && $z->name !== 'App');
// now that we're at the right depth, hop to the next <App/> until the end of the tree
while ($z->name === 'App')
{
// either one should work
//$node = new SimpleXMLElement($z->readOuterXML());
$node = simplexml_import_dom($doc->importNode($z->expand(), true));
// now you can use $node without going insane about parsing
var_dump($node->element_1);
// go to next <product />
$z->next('App');
}
?>
When I load the PHP file, no errors appear -- the page is just blank. My XML data structure is below...
<App action="A" id="1">
<BaseVehicle id= "17491"/>
<Note><![CDATA[License Plate Lamp]]></Note>
<Qty>.000</Qty>
<PartType id= "10043"/>
<Part>W0133-1620896</Part>
<Product>
<PartNumber>W0133-1620896</PartNumber>
<BrandID>OES</BrandID>
<BrandDescription><![CDATA[Genuine]]></BrandDescription>
<WorldpacCategoryID>P9032</WorldpacCategoryID>
<Price>29.85</Price>
<ListPrice>33.17</ListPrice>
<Available>Y</Available>
<OEFlag>OEM</OEFlag>
<Weight>.10</Weight>
<Height>.7</Height>
<Width>4.4</Width>
<Length>4.4</Length>
<SellingIncrement>1</SellingIncrement>
<Popularity>D</Popularity>
<ImageURL><![CDATA[http://img.eautopartscatalog.com/live/W01331620896OES.JPG]]></ImageURL>
<ThumbURL><![CDATA[http://img.eautopartscatalog.com/live/thumb/W01331620896OES.JPG]]></ThumbURL>
</Product>
<ImageURL><![CDATA[http://img.eautopartscatalog.com/live/W01331620896OES.JPG]]></ImageURL>
<ThumbURL><![CDATA[http://img.eautopartscatalog.com/live/thumb/W01331620896OES.JPG]]></ThumbURL>
</App>
Is it stalling because of the size of the file? If so, isn't XMLReader supposed to work for large XML files? If nothing else, what other options would I have?
I suppose I could load the XML data into a database if needed and then use SELECT queries to build the array for the MAGMI import. Though I'm not sure how to import an XML file into a SQL database. If need be, I'll be happy to get guidance with that.
I am trying to write a code where it will find a specific element in my XML file and then change the value of the text node. The XML file has different namespaces. Till now, I have managed to register the namespaces and also echo the text node of the element, which I want to change.
<?php
$xml = simplexml_load_file('getobs.xml');
$xml->registerXPathNamespace('g','http://www.opengis.net/gml');
$result = $xml->xpath('//g:beginPosition');
foreach ($result as $title) {
echo $title . "\n";
}
?>
My question is: How can I change the value of this element using SimpleXML? I tried to use the nodeValue command but I am not able to make it work.
This is a part of the XML:
<sos:GetObservation xmlns:sos="http://www.opengis.net/sos/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" service="SOS" version="1.0.0" srsName="urn:ogc:def:crs:EPSG:4326">
<sos:offering>urn:gfz:cawa:def:offering:meteorology</sos:offering>
<sos:eventTime>
<ogc:TM_During xmlns:ogc="http://www.opengis.net/ogc" xsi:type="ogc:BinaryTemporalOpType">
<ogc:PropertyName>urn:ogc:data:time:iso8601</ogc:PropertyName>
<gml:TimePeriod xmlns:gml="http://www.opengis.net/gml">
<gml:beginPosition>2011-02-10T01:10:00.000</gml:beginPosition>
Thanks
Dimitris
In the end I managed to do it by using the PHP XML DOM.
Here is the code that I used in order to change the text node of a specific element:
<?php
// create new DOM document and load the data
$dom = new DOMDocument;
$dom->load('getobs.xml');
//var_dump($dom);
// Create new xpath and register the namespace
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('g','http://www.opengis.net/gml');
// query the result amd change the value to the new date
$result = $xpath->query("//g:beginPosition");
$result->item(0)->nodeValue = 'sds';
// save the values in a new xml
file_put_contents('test.xml',$dom->saveXML());
?>
Not wanting to switch from the code I've already made for SimpleXML, I found this solution:
http://www.dotdragnet.com/forum/index.php?topic=3979.0
Specificially:
$numvotes = $xml->xpath('/gallery/image[path="'.$_GET["image"].'"]/numvotes');
...
$numvotes[0][0] = $votes;
Hope this helps!