The problem i was having is the Root XML was being produced every time it writes to the XML.
The Main issue was setting up Child and Defining the Root. From the help of Łza
I now understand the Root XML Node is ignored.
So then you setup and create a Child and then add your content, And example of the correct format is.
$xml = simplexml_load_file('FILENAME.xml'); // Load XML File Need to add IF Statment to create if does not exist
$result = $xml->addchild('Result'); // Ignore Root NODE and Add Child Results
$result->addChild('Time', gmdate('D-M-Y -H:i:s')); // Rest of the below adds Child to Result and outputs results
$result->addChild('Channel', $Site);
$result->addChild('Type', '**');
$result->addChild('Process', $Status);
$result->addChild('SKU', $code->SKU);
$result->addChild('item', $item);
$result->addChild('Status', '$Feedback');
$result->addChild('ErrorID', '$Error');
$result->addChild('Message', '$Message');
$xml->asXml('FILENAME.xml'); //Write to file would be
// All of the above Code is using variables from another part of the script
The output would be
<Root>
<Result>
<Time>Fri-May-2013 -09:15:22</Time>
<Channel>20</Channel>
<Type>**</Type>
<Process>Update</Process>
<SKU>98746524765</SKU>
<Item/>
<Status>Problem</Status>
<ErrorID>999-Error</ErrorID>
<Message>Unknown file format support</Message>
</Result>
<Result>
<Time>Fri-May-2013 -09:15:22</Time>
<Channel>20</Channel>
<Type>**</Type>
<Process>Update</Process>
<SKU>5412254785</SKU>
<Item/>
<Status>Problem</Status>
<ErrorID>123-Error</ErrorID>
<Message>Invalid Item</Message>
</Result>
</Root>
Thanks
Try to use SimpleXMLElement library instead hardcoded xml creation. This is maybe more complicate to use at the begining, but much more safe (I mean avoid possible errors in xml structure when you hardcode the xml) and easy to use when you just get start to use it.
And easy to add/remove nodes, childnodes.
This is an example for your code:
$xml = new SimpleXMLElement('<xml/>');
$data = $xml->addChild('data');
$result = $data->addChild('Result');
$result->addChild('Time', gmdate('D-M-Y -H:i:s'));
$result->addChild('Channel', $SiteID);
// ... and the same way create all your xml nodes.
// if you want add next <result> node witch all elements repeat the code, (or put it in loop if you want more <result> elements):
$result = $data->addChild('Result');
$result->addChild('Time', gmdate('D-M-Y -H:i:s'));
$result->addChild('Channel', $SiteID);
// and after create all nodes save the file:
$xml->asXml('DHError.xml');
above code will create xml:
<xml>
<data>
<Result>
<Time>Fri-May-2013 -12:14:39</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:14:39</Time>
<Channel>data</Channel>
</Result>
</data>
</xml>
Thats it. Then if you need to load and process the xml it would be easy:
To load the File simply use:
$xml2 = simplexml_load_file('DHError.xml');
// to add new node <Result>:
$resultNext = $xml2->data->addchild('Result');
$resultNext->addChild('Time', gmdate('D-M-Y -H:i:s'));
$resultNext->addChild('Channel', $SiteID);
//and save file
$xml2->asXml('DHError.xml');
this create a xml:
<?xml version="1.0" ?>
<xml>
<data>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
</data>
</xml>
Related
I want to add Data2 as a child of Request, but instead it gets added as a child of Data.
class xml{
public function __construct(){
$this->request_xml = new SimpleXMLElement("<Request></Request>");
$this->request_xml->addAttribute('RequestType', "1");
$this->request_xml->addChild("Data");
$this->request_xml->addChild("Data2");
var_dump($this->request_xml->asXml());
}
}
$object = new xml();
the result is:
<request>
<data>
<data2></data2>
</data>
</request>
I want
<request>
<data></data>
<data2></data2>
</request>
What am I missing?
Thanks!
The XML output is:
<?xml version="1.0"?>
<Request RequestType="1"><Data/><Data2/></Request>
In other words the Data and Data2 elements are siblings, but short empty tags. If the browser loads it as HTML it will try to repair the missing closing tags. This will not happen if it is parsed as XML. Make sure that you send the correct content type header:
header('Content-type: application/xml; charset=utf-8');
If you import the SimpleXMLElements into DOM (or better generate the document using DOM in the first place) you get a more options for saving the XML.
$element = dom_import_simplexml($request_xml);
echo $element->ownerDocument->saveXml(NULL, LIBXML_NOEMPTYTAG);
Output:
<?xml version="1.0"?>
<Request RequestType="1"><Data></Data><Data2></Data2></Request>
I am trying to read the value for 3 specific XML nodes (bill_codes, sent_total, clicked_unique_total) I have done a lot of testing and I feel like I need someone with fresh eyes to look at this and help me find out what I no longer see..
I am using the simplexml_load_string function to load the XML into an array..
Here is the code that I have so far:
$xml = simplexml_load_string($content);
echo $xml->methodResponse->item->responseData->message_data->message->bill_codes;
This is the XML that I am using (comes from an API Call so I have no access to modifying/updating the structure of the XML)
<?xml version="1.0" encoding="utf-8"?>
<methodResponse>
<item>
<methodName>
<![CDATA[legacy.message_stats]]>
</methodName>
<responseData>
<message_data>
<message id="2345456">
<message_subject>
<![CDATA[#1 Item You Should Be Hoarding in 2015]]>
</message_subject>
<date_sent>2014-12-18 04:01:34</date_sent>
<message_notes>
<![CDATA[Sample Notes]]>
</message_notes>
<withheld_total>0</withheld_total>
<globally_suppressed>0</globally_suppressed>
<suppressed_total>0</suppressed_total>
<bill_codes>
<![CDATA[8578]]>
</bill_codes>
<sent_total>734273</sent_total>
<link_append_statement/>
<timezone/>
<message_name>
<![CDATA[Sample Message Name]]>
</message_name>
<optout_total>4054</optout_total>
<optout_rate_total>0.55</optout_rate_total>
<clicked_total>5363</clicked_total>
<clicked_unique>4350</clicked_unique>
<clicked_rate_unique>13.71</clicked_rate_unique>
<campaign_id>228640</campaign_id>
<campaign_type>C</campaign_type>
<included_groups>
<segment id="1208891">
<![CDATA[Segment Name Here]]>
</segment>
</included_groups>
<included_smartlists></included_smartlists>
<excluded_groups></excluded_groups>
<excluded_smartlists></excluded_smartlists>
<attributes></attributes>
<link id="40278272">
<has_name>1</has_name>
<clicked_unique_total>4350</clicked_unique_total>
</link>
</message>
</message_data>
</responseData>
<responseNum>
<![CDATA[1]]>
</responseNum>
<responseCode>
<![CDATA[201]]>
</responseCode>
</item>
</methodResponse>
No need to include the parent, just start with the ->item:
echo $xml->item->responseData->message_data->message->bill_codes;
Sample Output
Let's say my XML is like this:
<?xml version="1.0"?>
<lists>
<list
path=".">
<entry
kind="dir">
<name>Assignment1.1</name>
<commit
revision="1668">
<author>netid</author>
<date>2011-09-07T03:03:58.367692Z</date>
</commit>
</entry>
<entry
kind="file">
<name>Assignment1.1/.classpath</name>
<size>397</size>
<commit
revision="1558">
<author>netid</author>
<date>2011-09-06T17:00:52.998920Z</date>
</commit>
.
.
.
</list>
</lists>
And I store it in a SimpleXML object using
$xml_list = simplexml_load_file(dirname(__FILE__).'/svn_list.xml');
How would I access for example, the revision variable containing 1558?
I can't seem to figure it out using a combination of echo and print_r.
SimpleXML uses a set of classes which implement iterators to work through them, so you can loop through each node using foreach, however the easiest way to navigate the XML once it's loaded is by using SimpleXMLElement::xPath(). To get revision 1558, you can make the following call:
$commit = $xml_list->xpath('//list/entry/commit[#revision="1558"]');
This will return you the nodes underneath <commit revision="1558">, and you can then access them from the $commit variable, which extends ArrayObject.
To get the actual content of the <author> element, you must do the following:
print((string)$commit[0]->author);
SimpleXMLElement instances need to be cast to a type to expose their actual values.
Also, if you want to dump the content of $commit to see its child nodes, the easiest way is to call the asXml() method as follows:
print($commit[0]->asXml());
You are facing difficulties because you have error on your XML file , The </entry> tag was not closed.
You could traverse like this.
<?php
$xml='<lists>
<list>
<entry
kind="dir">
<name>Assignment1.1</name>
<commit
revision="1668">
<author>netid</author>
<date>2011-09-07T03:03:58.367692Z</date>
</commit>
</entry>
<entry
kind="file">
<name>Assignment1.1/.classpath</name>
<size>397</size>
<commit
revision="1558">
<author>netid</author>
<date>2011-09-06T17:00:52.998920Z</date>
</commit>
</entry>
</list>
</lists>';
$xml = simplexml_load_string($xml);
foreach ($xml->list->entry[0]->commit->attributes() as $a=>$v)
{
echo $v;
}
OUTPUT :
1668
I was tesing with a simple example of how to display XML in browser using PHP and found this example which works good
<?php
$xml = new DOMDocument("1.0");
$root = $xml->createElement("data");
$xml->appendChild($root);
$id = $xml->createElement("id");
$idText = $xml->createTextNode('1');
$id->appendChild($idText);
$title = $xml->createElement("title");
$titleText = $xml->createTextNode('Valid');
$title->appendChild($titleText);
$book = $xml->createElement("book");
$book->appendChild($id);
$book->appendChild($title);
$root->appendChild($book);
$xml->formatOutput = true;
echo "<xmp>". $xml->saveXML() ."</xmp>";
$xml->save("mybooks.xml") or die("Error");
?>
It produces the following output:
<?xml version="1.0"?>
<data>
<book>
<id>1</id>
<title>Valid</title>
</book>
</data>
Now I have got two questions regarding how the output should look like.
The first line in the xml file '', should not be displayed, that is it should be hidden
How can I display the TextNode in the next line. In total I am exepecting an output in this fashion
<data>
<book>
<id>1</id>
<title>
Valid
</title>
</book>
</data>
Is that possible to get the desired output, if so how can I accomplish that.
Thanks
To skip the XML declaration you can use the result of saveXML on the root node:
$xml_content = $xml->saveXML($root);
file_put_contents("mybooks.xml", $xml_content) or die("cannot save XML");
Please note that saveXML(node) has a different output from saveXML().
First question:
here is my post where all usable threads with answers are listed: How do you exclude the XML prolog from output?
Second question:
I don't know of any PHP function that outputs text nodes like that.
You could:
read xml using DomDocument and save each node as string
iterate trough nodes
detect text nodes and add new lines to xml string manually
At the end you would have the same XML with text node values in new line:
<node>
some text data
</node>
$xml = '<?xml version="1.0"?>
<entries>
<response>
<category>client</category>
<action>Greeting</action>
<code>1000</code>
<msg>Your Connection with API Server is Successful</msg>
<resData>
<data name="svDate">2010-10-10 02:27:14</data>
</resData>
</response>
<response>
<category>client</category>
<action>Login</action>
<code>1000</code>
<msg>Command completed successfully</msg>
<value>L116:no value</value>
</response>
<response>
<category>domain</category>
<action>InfoDomain</action>
<code>1000</code>
<msg>Command completed successfully</msg>
<value>L125:no value</value>
<resData>
<data name="domain">google.com</data>
<data name="crDate">2004-12-16</data>
<data name="exDate">2013-12-16</data>
</resData>
</response>
</entries>';
$xml = simplexml_load_string($xml);
$domain = $xml->response[2]->resData[0]->data[0];
$crdate = $xml->response[2]->resData[0]->data[1];
$exdate = $xml->response[2]->resData[0]->data[2];
With the above code i can get the values.
But how can i get the values by attribute value?
For example i want to get the values with something like this:
$domain = $xml->response[2]->resData[0]->data["domain"];
$crdate = $xml->response[2]->resData[0]->data["crdate"];
$exdate = $xml->response[2]->resData[0]->data["exdate"];
One more question.
If i have two elements with the same name?
For example i would like to parse the dns. How could i do it?
The xml code is like this:
<?xml version="1.0"?>
<entries>
<response>
<category>client</category>
<action>Greeting</action>
<code>1000</code>
<msg>Your Connection with API Server is Successful</msg>
<resData>
<data name="svDate">2010-10-10 02:27:14</data>
</resData>
</response>
<response>
<category>client</category>
<action>Login</action>
<code>1000</code>
<msg>Command completed successfully</msg>
<value>L116:no value</value>
</response>
<response>
<category>domain</category>
<action>InfoDomain</action>
<code>1000</code>
<msg>Command completed successfully</msg>
<value>L125:no value</value>
<resData>
<data name="domain">google.com</data>
<data name="crDate">2004-12-16</data>
<data name="exDate">2013-12-16</data>
<data name="dns">ns1.google.com</data>
<data name="dns">ns2.google.com</data>
</resData>
</response>
</entries>
As you can see the ns1 and ns2 have the same name. name="dns".
How can i parse each one in a different variable?
Thank you!
With the element["attribute"] syntax, attribute is the name of an attribute on the element. It is not the value of some randomly chosen attribute belonging to an element.
The example below creates an array containing a mapping for the data elements of name attribute value to text value.
$data = array();
foreach ($xml->response[2]->resData->data as $d) {
$data[strtolower($d['name'])] = (string) $d;
}
// Now you can access the values via $data['domain'], $data['crdate'], etc.
Nick, your code expects XML structured like:
<resData>
<data domain="google.com" crdate="2004-12-16" exdate="2013-12-16" />
</resData>
Edit due to question change
In a marvelous dose of eating my own words, due to the change in the question an XPath approach would be more appropriate (don't you love OPs who do that?).
You can easily get an array of the name="dns" elements with a basic XPath expression.
$xml = simplexml_load_string($xml);
$dns = $xml->xpath('response[category="domain"]/resData/data[#name="dns"]');
You can use XPath to to do queries against your XML, e.g.
$entries = simplexml_load_string($xml);
$dataElements = $entries->xpath('/entries/response/resData/data[#name="dns"]');
foreach ($dataElements as $dataElement) {
echo $dataElement;
}
The above XPath finds all <data> elements with a name attribute of "dns" that are direct children of the given element hierarchy, e.g.
<entries>
…
<response>
…
<resData>
…
<data name="dns">
IMO this is easier and more appropriate than having the extra step of copying over the values into an array which would disconnect it from the actual DOM tree and which you would have to repeat for all the elements you want to map this way. XPath is built-in. You just query and get the result.
Because $dataElements is an array, you can also access the elements in it individually with
echo $dataElements[0]; // prints "ns1.google.com"
echo $dataElements[1]; // prints "ns2.google.com"
Note that the $dataElements array actually contains SimpleXmlElements connected to the main document. Any changes you do to them will also be reflected in the main document.