XML Error - Extra content at end of document? - php

My XML is looking perfect but not working. I get a error on line 2 at column 1: Extra content at the end of the document
Other answers from other posts aren't helping
<?php
$csv = array_map(function($v){return str_getcsv($v, "|");},file('quotes.csv'));
$hdrs = array_shift($csv);
$count = 0;
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml .= '<quotes>';
foreach($csv as $k=>$v) {
$xml .= '<record id="' . ++$count .'">';
foreach($hdrs as $h=>$i) {
$xml .= '<' . $i . '>' . htmlspecialchars($v[$h], ENT_XML1, 'UTF-8') . '</' . $i . '>';
}
$xml .= '</record>';
}
$xml .= '</quotes>';
header ("Content-Type:text/xml");
echo $xml;
?>
Expected Output
<quotes>
<record id="1">
<quote>There is no remedy but to love more.</quote>
<source>Henry David Thoreau</source>
<dob-dod>1817-1862</dob-dod>
<wplink>http://en.wikipedia.org/wiki/Henry_David_Thoreau</wplink>
<wpimg>http://upload.wikimedia.org/wikipedia/commons/f/f0/Benjamin_D._Maxham_-_Henry_David_Thoreau_-_Restored.jpg</wpimg>
<category>love</category>
</record>
</quotes>

Use the XMLWriter, its quite easy and it will generate valid XML. Doing your own manually is always likely to cause little anomalies.
$lines = array_map(function($v){return str_getcsv($v, "|");},file('quotes.csv'));
$hdrs = array_shift($lines);
$count = 0;
header ("Content-Type:text/xml");
$xml = new XMLWriter();
$xml->openUri('php://stdout');
$xml->startDocument();
$xml->setIndent(true);
$xml->startElement('quotes');
foreach($lines as $line) {
$xml->startElement("record");
$xml->writeAttribute('id', ++$count);
foreach($hdrs as $i=>$name) {
$xml->writeElement($name, htmlspecialchars($line[$i], ENT_XML1, 'UTF-8') );
}
$xml->endElement();
}
$xml->endElement();
$xml->endDocument();
$xml->flush();
The XMLWriter Docs in the manual
Using this data
source|dob-dod|wplink|wpimg|category
Henry David Thoreau|1817-1862|http://en.wikipedia.org/wiki/Henry_David_Thoreau|http://upload.wikimedia.org/wikipedia/commons/f/f0/Benjamin_D._Maxham_-_Henry_David_Thoreau_-_Restored.jpg|love
Sir Benjamin Baker|1840-1907|https://en.wikipedia.org/wiki/Benjamin_Baker_(engineer)|https://en.wikipedia.org/wiki/Benjamin_Baker_(engineer)#/media/File:BBaker.jpg|Engineers
We get this result
<?xml version="1.0"?>
<quotes>
<record id="1">
<source>Henry David Thoreau</source>
<dob-dod>1817-1862</dob-dod>
<wplink>http://en.wikipedia.org/wiki/Henry_David_Thoreau</wplink>
<wpimg>http://upload.wikimedia.org/wikipedia/commons/f/f0/Benjamin_D._Maxham_-_Henry_David_Thoreau_-_Restored.jpg</wpimg>
<category>love</category>
</record>
<record id="2">
<source>Sir Benjamin Baker</source>
<dob-dod>1840-1907</dob-dod>
<wplink>https://en.wikipedia.org/wiki/Benjamin_Baker_(engineer)</wplink>
<wpimg>https://en.wikipedia.org/wiki/Benjamin_Baker_(engineer)#/media/File:BBaker.jpg</wpimg>
<category>Engineers</category>
</record>
</quotes>

Related

Load Foreach loop into string as XML

I'm trying to load a foreach into a string formatted as XML.
I've tried as follows
<?php
$data = '<?xml version="1.0" encoding="utf-8" ?><markers>';
foreach ($entries as $entry => $marker ) {
'<marker name="' . $marker->getName() . '"'.'lat="' . $marker->getLatitude() . '"'.'lng="' . $marker->getLongitude() . '"'.'address="' . $marker->getAddressLineOne() . '"'.'address2="' . $marker->getAddressLineTwo() . '"'.'city="' . $marker->getCitySuburb() . '"'.'state="' . $marker->getState('fieldName') . '"'.'postal="' . $marker->getPostCode() . '"'.'country="' . $marker->getCountry('fieldName') . '"'.'phone="' . $marker->getPhone() . '"'.'email="' . $marker->getEmail() . '"'.'web="' . $marker->getWebSite() . '"'.'/>';
}
'</markers>';
?>
But what I end up getting is:
nothing in the $data variable
for some reason each item is nesting in the previous item
Basically I would like to achieve the following result:
<?php
$data = '<?xml version="1.0" encoding="utf-8"?>
<markers>
<marker name="Chipotle Minneapolis" lat="44.947464" lng="-93.320826" category="Restaurant" address="3040 Excelsior Blvd" address2="" city="Minneapolis" state="MN" postal="55416" country="US" phone="612-922-6662" email="info#chipotle.com" web="http://www.chipotle.com" />
<marker name="Chipotle St. Louis Park" lat="44.930810" lng="-93.347877" category="Restaurant" address="5480 Excelsior Blvd." address2="" city="St. Louis Park" state="MN" postal="55416" country="US" phone="952-922-1970" email="info#chipotle.com" web="http://www.chipotle.com" />
<marker name="Chipotle Minneapolis" lat="44.9553438" lng="-93.29719699999998" category="Restaurant, Bar" address="2600 Hennepin Ave." address2="" city="Minneapolis" state="MN" postal="55404" country="US" phone="612-377-6035" email="info#chipotle.com" web="http://www.chipotle.com" />
</markers>';
?>
Thanks
Said
Revised code
<?php
$data = simplexml_load_string("<markers />");
foreach ($entries as $entry => $marker ) {
$newMarker = $data->addChild("marker");
$newMarker->addAttribute("name", $marker->getName());
$newMarker->addAttribute("lat", $marker->getLatitude());
$newMarker->addAttribute("lng", $marker->getLongitude());
$newMarker->addAttribute("state", $marker->getPostCode());
}
echo $data->asXML();
?>
<?php
echo var_dump($data);
?>
After the first line where you use
$data = '<?xml version="1.0" encoding="utf-8" ?><markers>';
you don't add any of the further text onto the variable. You need to use something like
$data .='<marker name="' . $marker->getName() .`
(with the dot before the = to add the value), you also need to be careful with the spacing and the quotes.
I would also recommend using something like SimpleXML to build the XML rather than using put text, it is longer code but safer. Something like...
$xml = simplexml_load_string("<markers />");
foreach ($entries as $entry => $marker ) {
$newMarker = $xml->addChild("marker");
$newMarker->addAttribute("name", $marker->getName());
$newMarker->addAttribute("lat", $marker->getLatitude());
$newMarker->addAttribute("lng", $marker->getLongitude());
// Repeat for all attributes
}
echo $xml->asXML();
Since it's not clear to me how $entries are generated, I'll make up some data in couple of arrays and create a simplified version of your expected output, with the help of a function borrowed from here. Obviously, you'll need to modify it to fit your actual code.
$data = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<markers>
</markers>
XML;
$xml = simplexml_load_string($data);
function sxml_append(SimpleXMLElement $to, SimpleXMLElement $from) {
$toDom = dom_import_simplexml($to);
$fromDom = dom_import_simplexml($from);
$toDom->appendChild($toDom->ownerDocument->importNode($fromDom, true));
}
$stores = ["Chipotle Chicago","Chipotle St. Louis Park","Chipotle Minneapolis"];
$locations = ["Chicago","St. Louis Park","Minneapolis"];
$destination = $xml->xpath('//markers');
foreach(array_combine($stores, $locations) as $store => $location) {
$tm = simplexml_load_string('<marker name="xxx" city="yyy"/>');
$tmxml = $tm->xpath('//marker')[0];
$tmxml['name'] = $store;
$tmxml['city'] = $location;
sxml_append($destination[0], $tmxml);
}
echo $xml->asXML();
Output:
<?xml version="1.0" encoding="UTF-8"?>
<markers>
<marker name="Chipotle Chicago" city="Chicago" />
<marker name="Chipotle St. Louis Park" city="St. Louis Park" />
<marker name="Chipotle Minneapolis" city="Minneapolis" />
</markers>

how to dynamically change the value of an xml node with php

<?xml version="1.0" encoding="UTF-8" ?>
<SMS>
<authentification>
<username>xxxx</username>
<password>xxxx</password>
</authentification>
<recipients>
<number>8309042932</number>
</recipients>
</SMS>
my number node has a dynamically generated numbers for different people, i want to load all the numbers, but am getting only the last number.
Code used to create the xml string:
<?xml version="1.0" encoding="UTF-8" ?>
$xmlstring =
"<SMS>
<authentification>
<username>xxxx</username>
<password>xxxx</password>
</authentification>
<recipients>";
foreach($gsmnumbers as $number) {
$number = explode(",", $number);
foreach($number as $num) {
$count = count($num);
for($i = 0; $i < $count; $i++) {
$xmlHalf = "<gsm>$num</gsm>";
}
}
}
$xmlSecondHalf = "</recipients> </SMS>";
Try this, all sorts of mistakes in your code.
Basically if you use the .= syntax, you can concatenate to the end of an existing string where just using = will replace the string with the new value.
Also the <?xml version="1.0" encoding="UTF-8" ?> need to be in the string to identify it as a valid XML string.
$xmlstring = '
<?xml version="1.0" encoding="UTF-8" ?>
<SMS>
<authentification>
<username>xxxx</username>
<password>xxxx</password>
</authentification>
<recipients>';
foreach($gsmnumbers as $number) {
$nums = explode(",", $number);
foreach($nums as $num) {
$xmlstring .= "<gsm>$num</gsm>";
}
}
$xmlstring .= "</recipients></SMS>";
Don't write XML as string, use a XML library for that. A library normally prevents you from shooting into your own foot when it comes to creating XML. It also makes your code more readable. Example:
// process and transform input data
$gsmnumbers = ['1234,5678,9012'];
$gsms = [];
foreach ($gsmnumbers as $number) {
$nums = explode(",", $number);
foreach ($nums as $num) {
$gsms[] = $num;
}
}
// create XML
$request = new SimpleXMLElement('<SMS/>');
$authentication = $request->addChild('authentification');
$authentication->username = 'XXXX';
$authentication->password = 'XXXX';
$recipients = $request->addChild('recipients');
foreach ($gsms as $gsm) {
$recipients->addChild('gsm', $gsm);
}
echo $request->asXML();
Example output:
<?xml version="1.0"?>
<SMS><authentification><username>XXXX</username><password>XXXX</password></authentification><recipients><gsm>1234</gsm><gsm>5678</gsm><gsm>9012</gsm></recipients></SMS>

retrieve atom:id from XML

How do you retrieve the value in atom:id from a XML document?
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<atom:id>http://www.google.com/m8/feeds/profiles/domain/mydomain.com/full/test</atom:id>
</atom:entry>
You can use SimpleXML and XPath for that:
$xml = <<<XML
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<atom:id>http://www.google.com/m8/feeds/profiles/domain/mydomain.com/full/test</atom:id>
</atom:entry>
XML;
$xml = new SimpleXMLElement($xml);
$result = $xml->xpath('/atom:entry/atom:id');
foreach ($result as $curResult)
{
echo __FILE__ . ':' . __LINE__ . '<pre>' . print_r($curResult, 1) . '</pre>';
}
You could use simple_xml and an xpath query. Like so:
$xml = <<<EOF
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
<atom:id>http://www.google.com/m8/feeds/profiles/domain/mydomain.com/full/test</atom:id>
</atom:entry>
EOF;
$doc = simplexml_load_string($xml);
$el = $doc->xpath('//atom:id');
echo (string)$el[0];
(obviously that's without error checking and all)

PHP access XML node element

I am trying to edit some XML with PHP. Currently the XML looking something like:
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Main Title</title>
<link>http://exmaple.com</link>
<description> blahblahblah </description>
<language>en</language>
<item>
<title>Tite1</title>
<link>http://www.example.com (THIS IS WHAT I WANT)</link>
<description>blah blah blah</description>
</item>
.
.
.
</channel>
</rss>
I've tried to access the 2nd level link but my code only changes the first Link node value. Here is the code:
$xml->load('http://www.google.com/doodles/doodles.xml');
$element = $xml->getElementsByTagName('channel')->item(0);
$secondlvl = $element->getElementsByTagName('item')->item(0);
$2ndlevellinknode = $element->getElementsByTagName('link')->item(0);
$2ndlevellinknode->nodeValue = $newvalue;
Any suggestions? Also is it possible to use this line of code in a for loop like this
for ($i = 0; $i <= 20; $i++) {
$element = $xml->getElementsByTagName('channel')->item(0);
$secondlvl = $element->getElementsByTagName('item')->item(0);
$2ndlevellinknode = $element->getElementsByTagName('link')->item($i);
$2ndlevellinknode->nodeValue = $newvalue;
}
this should give you an idea.
$f = simplexml_load_file('test.xml');
print $f->channel->title . "\n";
print $f->channel->link . "\n";
print $f->channel->description . "\n";
foreach($f->channel->item as $item) {
print $item->title . "\n";
}

how i can use SAX parser

This is what the result should look like when i parse it through a SAX parser
http://img13.imageshack.us/img13/6950/75914446.jpg
This is the XML source code from which i need to generate the display:
<orders>
<order>
<count>37</count>
<price>49.99</price>
<book>
<isbn>0130897930</isbn>
<title>Core Web Programming Second Edition</title>
<authors>
<count>2</count>
<author>Marty Hall</author>
<author>Larry Brown</author>
</authors>
</book>
</order>
<order>
<count>1</count>
<price>9.95</price>
<yacht>
<manufacturer>Luxury Yachts, Inc.</manufacturer>
<model>M-1</model>
<standardFeatures oars="plastic" lifeVests="none">false</standardFeatures>
</yacht>
</order>
<order>
<count>3</count>
<price>22.22</price>
<book>
<isbn>B000059Z4H</isbn>
<title>Harry Potter and the Order of the Phoenix</title>
<authors>
<count>1</count>
<author>J.K. Rowling</author>
</authors>
</book>
</order>
i really have no clue how to code the functions but i have just set up the parser
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($xmlParser, 'startElement', 'endElement');
xml_set_character_data_handler($xmlParser, 'HandleCharacterData');
$fileName = 'orders.xml';
if (!($fp = fopen($fileName, 'r'))){
die('Cannot open the XML file: ' . $fileName);
}
while ($data = fread($fp, 4096)){
$parsedOkay = xml_parse($xmlParser, $data, feof($fp));
if (!$parsedOkay){
print ("There was an error or the parser was finished.");
break;
}
}
xml_parser_free($xmlParser);
function startElement($xmlParser, $name, $attribs)
{
}
function endElement($parser, $name)
{
}
function HandleCharacterData($parser, $data)
{
}
A SimpleXML-based recursive approach might be a simpler solution perhaps, something like:
$xml = new SimpleXmlElement($data);
function renderTree(SimpleXmlElement $xml, $depth = 0) {
$output = str_repeat('| ', $depth);
$output .= '+-' . $xml->getName();
if (sizeof($xml->attributes()) > 0) {
$attrs = '';
foreach ($xml->attributes() as $key => $value) {
$attrs .= $key . '=' . $value . ', ';
}
$output .= ' (' . trim($attrs, ', ') . ')';
}
$output .= ': ' . (string)$xml;
$output .= '<br />';
if ($xml->count() > 0) {
foreach ($xml->children() as $child) {
$output .= renderTree($child, $depth + 1);
}
}
return $output;
}
echo renderTree($xml);
Would render the tree as in the example image.

Categories