Xform xml parsing - php

In the xform xml i want to parse xml and access the id of node. It's the xform xml and i want to access the id of node test_geopoint (id=test_geopoint). But the node name will change for each xform xml.
<?php
$xmlstr = <<<XML <?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:jr="http://openrosa.org/javarosa" xmlns:orx="http://openrosa.org/xforms/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<h:head>
<h:title>test_geopoint</h:title>
<model>
<instance>
<test_geopoint id="test_geopoint">
<name/>
<geopoint/>
<meta>
<instanceID/>
</meta>
</test_geopoint>
</instance>
</h:body>
</h:html> $XML;
i tried the code like this, but not access the id of node after the <instance>.
$movies = new SimpleXMLElement($xmlstr);
echo $movies->model->instance->children()[0]['id'];
and
echo $movies->head->title->model->instance->children()[0]['id'];
How can retrieve id of node next to <instance> in php ?

The below example is the same as you expecting, use it the same way I have accessed, the name in a array. You are suppose to mention as instanceId
$xmlstr = <<<XML
<?xml version='1.0' standalone='yes'?>
<movies>
<movie>
<title>PHP: Behind the Parser</title>
<characters>
<character>
<name>Ms. Coder</name>
<actor>Onlivia Actora</actor>
</character>
<character>
<name>Mr. Coder</name>
<actor>El ActÓr</actor>
</character>
</characters>
<plot>
So, this language. It's like, a programming language. Or is it a
scripting language? All is revealed in this thrilling horror spoof
of a documentary.
</plot>
<great-lines>
<line>PHP solves all my web problems</line>
</great-lines>
<rating type="thumbs">7</rating>
<rating type="stars">5</rating>
</movie>
</movies>
XML;
$movies = new SimpleXMLElement($xmlstr);
echo '<pre>';
foreach($movies as $moviesdata){
foreach($moviesdata as $moviesdatavalues){
foreach($moviesdatavalues as $mdvk=>$mdn){
foreach($mdn as $c=>$v){
if($c == 'name'){
echo $v."\n";
}
}
}
}
}
?>

The default namespace is http://www.w3.org/2002/xforms so every unprefixed element descendant of html is in that namespace; including your instance data.
Try adding an empty namespace (xmlns="") to your instance data...
<model>
<instance>
<test_geopoint xmlns="" id="test_geopoint">
<name/>
<geopoint/>
<meta>
<instanceID/>
</meta>
</test_geopoint>
</instance>
<bind nodeset="/test_geopoint/name" type="string"/>
<bind nodeset="/test_geopoint/geopoint" type="geopoint"/>
<bind calculate="concat('uuid:', uuid())" nodeset="/test_geopoint/meta/instanceID" readonly="true()" type="string"/>
</model>

Related

php xml schema formatting

I have a php page what post's a xml to Heartinternet API and after a long time I have got it to work but now I cant find away to only pull only one part out of the replyed XML
$some_xml = '<?xml version="1.0"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:package="http://www.heartinternet.co.uk/whapi/package-2.2">
<command>
<info>
<package:info>
<package:id>171371a16973b1bf</package:id>
</package:info>
</info>
<extension>
<ext-package:preAuthenticate xmlns:ext-package="http://www.heartinternet.co.uk/whapi/ext-package-2.2"/>
</extension>
<clTRID>fac89208bea460fa3fef11b22a519cce</clTRID>
</command>
</epp>';
This is the code what is posted to the API. Full code can been seen here.
This is what I get back and can't figure out how to pull one line from the reply.
<?xml version='1.0'?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:ext-package="http://www.heartinternet.co.uk/whapi/ext-package-2.2">
<response>
<result code='1000'>
<msg>Command completed successfully</msg>
</result>
<resData>
<ext-package:redirectURL>http://bobcp.example.org/sso.cgi?session=LUB9UNbw6jTW</ext-package:redirectURL>
</resData>
<trID>
<clTRID>fac89208bea460fa3fef11b22a519cce</clTRID>
<svTRID>test-19272326601ef4c3bf6b64730d09c6cf</svTRID>
</trID>
</response>
</epp>
The one line I need to show is ext-package:redirectURL.
If anyone could help me or point me in the right direction to find how to sort this I would be grateful!
You can get the redirect url by registering the namespace urn:ietf:params:xml:ns:epp-1.0 and then you could use an xpath expression for example. In this case, I have chosen u as the prefix.
/u:epp/u:response/u:resData/ext-package:redirectURL
Using SimpleXML with your returned xml:
The response xml from the comments is slightly different. This is the updated code:
$returned_xml->registerXPathNamespace('u', 'urn:ietf:params:xml:ns:epp-1.0');
$returned_xml->registerXPathNamespace('ext-package', 'http://www.heartinternet.co.uk/whapi/ext-package-2.2');
$redirectUrl = $returned_xml->xpath('/u:epp/u:response/u:resData/ext-package:redirectURL');
echo $redirectUrl[0];
Or with DOMDocument:
$doc = new DOMDocument();
$doc->loadXML(file_get_contents("https://custom-hosting.co.uk/source/test.php"));
$xpath = new DOMXpath($doc);
$xpath->registerNamespace('u', 'urn:ietf:params:xml:ns:epp-1.0');
$xpath->registerNamespace('ext-package', 'http://www.heartinternet.co.uk/whapi/ext-package-2.2');
$redirectUrl = $xpath->query('/u:epp/u:response/u:resData/ext-package:redirectURL')->item(0)->nodeValue;
echo $redirectUrl;
That will give you for example:
http://bobcp.example.org/sso.cgi?session=LUB9UNbw6jTW
<?xml version='1.0'?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:ext-package="http://www.heartinternet.co.uk/whapi/ext-package-2.2">
<response>
<result code='1000'>
<msg>Command completed successfully</msg>
</result>
<resData>
<ext-package:redirectURL>http://bobcp.example.org/sso.cgi?session=LUB9UNbw6jTW</ext-package:redirectURL>
</resData>
<trID>
<clTRID>fac89208bea460fa3fef11b22a519cce</clTRID>
<svTRID>test-19272326601ef4c3bf6b64730d09c6cf</svTRID>
</trID>
</response>
</epp>
that is what i get sent back from the API its not in my test page at all
Full page source
if i add
$xml = simplexml_load_string($returned_xml);
$xml->registerXPathNamespace('u', 'urn:ietf:params:xml:ns:epp-1.0');
$redirectUrl = $xml->xpath('/u:epp/u:response/u:resData/ext-package:redirectURL');
echo $redirectUrl[0];
to the page i just get
XML Parsing Error: no element found
Location: https://custom-hosting.co.uk/
Line Number 1, Column 1:

XML Clone node in PHP

I have to clone an XML node and its childs and append it to a new XML in a specifics tag.
Ie:
Source XML:
<root>
<element>
<back>
<item1>ABC</item1>
<item2>DEF</item2>
<more>
<moreitem>GHI</moreitem>
</more
</back>
</element>
</root>
Destination XML:
<root>
<base1>
<item1>FOO</item1>
<item2>BAR</item2>
<base2>
**<back>From source XML and all its childs here</back>**
</base2>
</base1>
<root>
DOMXpath::evaluate() allows you to fetch nodes using Xpath expressions. DOMDocument::importNode() duplicates a node and imports a node into a target document. DOMNode::cloneNode() create a duplicate of node to add in the same document. DOMNode::appendChild() allows you to append the imported/cloned node.
$source = <<<'XML'
<root>
<element>
<back>
<item1>ABC</item1>
<item2>DEF</item2>
<more>
<moreitem>GHI</moreitem>
</more>
</back>
</element>
</root>
XML;
$target = <<<'XML'
<root>
<base1>
<item1>FOO</item1>
<item2>BAR</item2>
<base2>
</base2>
</base1>
</root>
XML;
$sourceDocument = new DOMDocument();
$sourceDocument->loadXml($source);
$sourceXpath = new DOMXpath($sourceDocument);
$targetDocument = new DOMDocument();
$targetDocument->loadXml($target);
$targetXpath = new DOMXpath($targetDocument);
foreach ($targetXpath->evaluate('/root/base1/base2[1]') as $targetNode) {
foreach ($sourceXpath->evaluate('/root/element/back') as $backNode) {
$targetNode->appendChild($targetDocument->importNode($backNode, TRUE));
}
}
echo $targetDocument->saveXml();
Output:
<?xml version="1.0"?>
<root>
<base1>
<item1>FOO</item1>
<item2>BAR</item2>
<base2>
<back>
<item1>ABC</item1>
<item2>DEF</item2>
<more>
<moreitem>GHI</moreitem>
</more>
</back>
</base2>
</base1>
</root>
Of course you can use XSLT, the native programming language to restructure XML documents to any nuanced needs. Specifically here, you require pulling XML content from an external source XML file. And PHP like other general purpose languages (Java, C#, Python, VB) maintain libraries for XSLT processing.
XSLT (save as .xsl or .xslt file to be used in PHP below and be sure Source and Destination XML files are in same directory)
<?xml version="1.0" ?>
<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>
<xsl:template match="back">
<back>
<xsl:copy-of select="document('Source.xml')"/>
</back>
</xsl:template>
</xsl:transform>
PHP (loading XML and XSL files externally but can be embedded as string)
$destinationdoc = new DOMDocument();
$doc1->load('Destination.xml');
$xsl = new DOMDocument;
$xsl->load('XSLTScript.xsl');
// Configure the transformer
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// Transform XML source
$newXml = $proc->transformToXML($doc1);
// Save output to file
$xmlfile = 'FinalOutput.xml';
file_put_contents($xmlfile, $newXml);
OUTPUT (using your above posted Source and Destination xml)
<?xml version="1.0" encoding="UTF-8"?>
<root>
<base1>
<item1>FOO</item1>
<item2>BAR</item2>
<base2>
<back>
<root>
<element>
<back>
<item1>ABC</item1>
<item2>DEF</item2>
<more>
<moreitem>GHI</moreitem>
</more>
</back>
</element>
</root>
</back>
</base2>
</base1>
</root>
This is an easy way to do this:
$src = new DOMDocument();
$dst = new DOMDocument();
$src->loadXML($src_xml);
$dst->loadXML($dst_xml);
$back = $src->getElementsByTagName('back')->item(0);
$base = $dst->getElementsByTagName('base2')->item(0);
$base->appendChild( $dst->importNode( $back, true ) );
echo $dst->saveXML();

PHP - how to parse STRING field (remove some XML elements and all namespaces)?

I really need a help about string parsing in field containg not valid XML value.
I will display current value with target value to put in string field.
I have a field $xmlString with this value (elements are NOT in the SEPERATE lines but in the SAME line; it is web service response so I do not have impact on response only on later parsing):
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv=" http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<p:queryBillingAccountResponse xmlns:p="http://www.ibm.com">
<ns0:customerAccount xmlns:ns0=" http://www.ibm.com/2009">
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</ns0:customerAccount>
</p:queryBillingAccountResponse>
</soapenv:Body>
</soapenv:Envelope>
I want this output if this is possible:
<queryBillingAccountResponse>
<customerAccount>
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</customerAccount>
</queryBillingAccountResponse>
So you will notice that I do not have first three lines (although they are not really seperate lines) and last two lines and I do not have namespaces defined for queryBilling AccountResponse and customer Account. I want these elements without namespace to be in string field. For both on start and end tag. I really need this output. How to parse this? I tried with SimpleXMLElement but could not parse it.
Thank you for your help
Updated output which can not be parsed by $xml = simplexml_load_string($text);
<<<XML
<?xml version="1.0" encoding="utf-8"?>
<Envelope>
<Body>
<queryBillingAccountResponse>
<customerAccount>
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</customerAccount>
</queryBillingAccountResponse>
</Body>
</Envelope>
XML>
In order to have a xml code that SimpleXML can understand, and since you don't need the namespaces declarations, the following code cleans the code before to apply it to simplexml_load_string
<?php
// if the XML comes from a file (or just assign the $text string)
$text = file_get_contents('myfile.xml');
$text = preg_replace('/(<\s*)\w+:/','$1',$text); // removes <xxx:
$text = preg_replace('/(<\/\s*)\w+:/','$1',$text); // removes </xxx:
$text = preg_replace('/\s+xmlns:[^>]+/','',$text); // removes xmlns:...
// the code should be clean enough for SimpleXML to parse it
$xml = simplexml_load_string($text);
// view the XML (and process it afterwards...)
print_r($xml);
To put the sample XML in a string (instead of a file)
<?php
$text = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv=" http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<p:queryBillingAccountResponse xmlns:p="http://www.ibm.com">
<ns0:customerAccount xmlns:ns0=" http://www.ibm.com/2009">
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</ns0:customerAccount>
</p:queryBillingAccountResponse>
</soapenv:Body>
</soapenv:Envelope>
XML;
$text = preg_replace('/(<\s*)\w+:/','$1',$text); // removes <xxx:
$text = preg_replace('/(<\/\s*)\w+:/','$1',$text); // removes </xxx:
$text = preg_replace('/\s+xmlns:[^>]+/','',$text); // removes xmlns:...
// the code should be clean enough for SimpleXML to parse it
$xml = simplexml_load_string($text);
// view the XML (and process it afterwards...)
print_r($xml);
To access elements, use -> (and [xx] for arrays), e.g
echo echo $xml->Body->queryBillingAccountResponse->customerAccount->ComponentCustomerAccount->Name . "\n";
will display
ADSL 4
SimpleXML doc

Adding child node in xml file

I have an xml file
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<settings>
<title>Calendar2</title>
<subTitle>Calendar2</subTitle>
</settings>
<events date="02-09-2010">
<event>
<title>HTML Tags</title>
<description>HTML Tags</description>
</event>
</events>
</xml>
How i can add another event inside events tag with respect to date
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<settings>
<title>Calendar2</title>
<subTitle>Calendar2</subTitle>
</settings>
<events date="02-09-2010">
<event>
<title>HTML Tags</title>
<description>HTML Tags</description>
</event>
<event>
<title>Another Title</title>
<description>Another description</description>
</event>
</events>
</xml>
i used this code
$xml_str = file_get_contents($xmlfile);
$xml = new SimpleXMLElement($xml_str);
$event = $xml->events->addChild('event');
$event->addChild('title', 'More Parser Stories');
$event->addChild('description', 'This is all about the people who make it work.');
file_put_contents($xmlfile, $xml->asXML());
But it will add to the first node.How i can add to events tag with date 02-09-2010
You'll have to query for the wanted <events> tag instead of taking the first one (which what $xml->events would simply return), using xpath to query the xml document is helpful here:
PHP Script:
<?php
$xml_str = file_get_contents('xmlfile');
$xml = new SimpleXMLElement($xml_str);
$wantedEventsTag = $xml->xpath('/xml/events[#date="02-09-2010"]');
$wantedEventsTag = $wantedEventsTag [0];//since above fun will return an array
$wantedEventsTag['attrname']='attrval';//Here's how to add an attribute
$event = $wantedEventsTag->addChild('event');
$event->addChild('title', 'More Parser Stories');
$event->addChild('description', 'This is all about the people who make it work.');
file_put_contents('xmlfile.xml', $xml->asXML());
Sample XML File with multiple <events> tags:
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<settings>
<title>Calendar2</title>
<subTitle>Calendar2</subTitle>
</settings>
<events date="01-01-1999">
</events>
<events>
</events>
<events date="02-09-2010">
<event>
<title>HTML Tags</title>
<description>HTML Tags</description>
</event>
<event>
<title>Another Title</title>
<description>Another description</description>
</event>
</events>
</xml>
the script xpath will match the required node, which we later use and add events subnodes to.
You'll need to use DOM instead, specifically DOMNode::insertBefore:
http://us.php.net/manual/en/domnode.insertbefore.php

Simple XML reading question (PHP)

I have the following XML file:
<?xml version="1.0" encoding="utf-8"?>
<SearchResults:searchresults xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.zillow.com/static/xsd/SearchResults.xsd /vstatic/279989c5e93d519f8d8f23d3f6cac661/static/xsd/SearchResults.xsd" xmlns:SearchResults="http://www.zillow.com/static/xsd/SearchResults.xsd">
<request>
<address>2114 Bigelow Ave</address>
<citystatezip>Seattle, WA</citystatezip>
</request>
<message>
<text>Request successfully processed</text>
<code>0</code>
</message>
<response>
<results>
<result>
<zpid>48749425</zpid>
<links>
<homedetails>http://www.zillow.com/homedetails/2114-Bigelow-Ave-N-Seattle-WA-98109/48749425_zpid/</homedetails>
<graphsanddata>http://www.zillow.com/homedetails/charts/48749425_zpid,1year_chartDuration/?cbt=3697699817560038867%7E3%7EQh4sEjEhBNEguUWA-0f22TvGUpBB7FpUkAZlBRy5_26R5PYjKDdVAA**</graphsanddata>
<mapthishome>http://www.zillow.com/homes/48749425_zpid/</mapthishome>
<myestimator>http://www.zillow.com/myestimator/Edit.htm?zprop=48749425</myestimator>
<myzestimator deprecated="true">http://www.zillow.com/myestimator/Edit.htm?zprop=48749425</myzestimator>
<comparables>http://www.zillow.com/homes/comps/48749425_zpid/</comparables>
</links>
<address>
<street>2114 Bigelow Ave N</street>
<zipcode>98109</zipcode>
<city>Seattle</city>
<state>WA</state>
<latitude>47.63793</latitude>
<longitude>-122.347936</longitude>
</address>
<zestimate>
<amount currency="USD">1112500</amount>
<last-updated>01/14/2010</last-updated>
<oneWeekChange deprecated="true"></oneWeekChange>
<valueChange duration="30" currency="USD">-77500</valueChange>
<valuationRange>
<low currency="USD">878875</low>
<high currency="USD">1145875</high>
</valuationRange>
<percentile>0</percentile>
</zestimate>
<localRealEstate>
<region id="271856" type="neighborhood" name="East Queen Anne">
<zindexValue>525,252</zindexValue>
<zindexOneYearChange>-0.104</zindexOneYearChange>
<links>
<overview>http://www.zillow.com/local-info/WA-Seattle/East-Queen-Anne/r_271856/</overview>
<forSaleByOwner>http://www.zillow.com/homes/fsbo/East-Queen-Anne-Seattle-WA/</forSaleByOwner>
<forSale>http://www.zillow.com/homes/for_sale/East-Queen-Anne-Seattle-WA/</forSale>
</links>
</region>
<region id="16037" type="city" name="Seattle">
<zindexValue>373,795</zindexValue>
<zindexOneYearChange>-0.064</zindexOneYearChange>
<links>
<overview>http://www.zillow.com/local-info/WA-Seattle/r_16037/</overview>
<forSaleByOwner>http://www.zillow.com/homes/fsbo/Seattle-WA/</forSaleByOwner>
<forSale>http://www.zillow.com/homes/for_sale/Seattle-WA/</forSale>
</links>
</region>
<region id="59" type="state" name="Washington">
<zindexValue>256,760</zindexValue>
<zindexOneYearChange>-0.074</zindexOneYearChange>
<links>
<overview>http://www.zillow.com/local-info/WA-home-value/r_59/</overview>
<forSaleByOwner>http://www.zillow.com/homes/fsbo/WA/</forSaleByOwner>
<forSale>http://www.zillow.com/homes/for_sale/WA/</forSale>
</links>
</region>
</localRealEstate>
</result>
</results>
</response>
</SearchResults:searchresults>
<!-- H:118 T:102ms S:1761 R:Fri Jan 15 10:52:49 PST 2010 B:3.0.79367-comp_rel_b -->
If you can't already tell, it's the standard output of the Zillow API. I want to store the data of information stored between certain tags, which I have learned is queried through the xpath.
For example, how would I query for the data in /SearchResults:searchresults/request/address? Doing something like this doesn't work when I echo/print the variable:
$xml = simplexml_load_file("-truncated XML URL-");
$result = $xml->xpath('/SearchResults:searchresults/request/address')
From what I understand, the $result variable should contain the value found nested in that %VALUE HERE%, correct? But it prints "Array", and when I print_r, it returns blank.
Here's a simple way to get that information:
<?php
$xml = simplexml_load_file("test.xml");
echo $xml->request->address;
?>
You need to use the full namespace not the short name.
Use http://simplepie.org
$xml = "yourxml code here";
$feed = new SimplePie();
$feed->set_raw_data($xml);
$feed->init();
$feed->handle_content_type();
echo $feed->get_adress();

Categories