I'm trying to get a product list for a client from a webservices xml file over to an SQL databse using a little php script, but I can't seem to get it to work.
The relevant code is as follows:
$c = 0;
...
$xml = simplexml_load_file($completeurl);
$listingsArray = $xml->listings->listing;
foreach($listingsArray as $listing){
$addition[0] = $listing[$c]->type;
$addition[1] = $listing[$c]->condition;
//etcetera
c = c + 1;
}
The XML file is formated like:
<inventory>
<listings>
<listing>
//tags for type, condition, etc
</listing>
</listings>
</inventory>
$completeurl is a string that contains the url of the xml file
$addition is an array that's defined earlier in the code
I've been working on this for a while now, but I can't seem to figure out where the error is in my code. The problem that I'm having is that $listingsArray should have close to 100 elements in it, but is constantly coming up with 0. Anybody see what I'm doing wrong?
EDIT: I tried changing
$listingsArray = $xml->listings->listing;
to
$listingsArray = $xml->listings;
But empty strings are still being written to the $addition array. A var_dump of listingsArray show that all of the information is in there, though.
If you want each listing i believe you are going a child too deep.
$listingsArray = $xml->listings->listing;
shoudl be
$listingsArray = $xml->listings;
foreach($listingsArray as $listing){
$addition[0] = $listing[$c]->type;
$addition[1] = $listing[$c]->condition;
//etcetera
}
additionally what is $c? It also helps to post your exact errors. When debugging inserting var_dumps is extremely helpful. If you think the probelm is $listingArray
print it out and see if it contains the data you want.
changing
$listingsArray = $xml->listings->listing;
to
$listingsArray = $xml->listings;
Should solve the issue. You set $listingsArray to the array of listings, then the foreach tries to go down another level
Related
Im trying to load search result from an library api using Search and Retrieve via URL (SRU) at : https://data.norge.no/data/bibsys/bibsys-bibliotekbase-bibliografiske-data-sru
If you see the search result links there, its looks pretty much like XML but when i try like i have before with xml using the code below, it just returns a empty object,
SimpleXMLElement {#546}
whats going on here?
My php function in my laravel project:
public function bokId($bokid) {
$apiUrl = "http://sru.bibsys.no/search/biblio?version=1.2&operation=searchRetrieve&startRecord=1&maximumRecords=10&query=ibsen&recordSchema=marcxchange";
$filename = "bok.xml";
$xmlfile = file_get_contents($apiUrl);
file_put_contents($filename, $xmlfile); // xml file is saved.
$fileXml = simplexml_load_string($xmlfile);
dd($fileXml);
}
If i do:
dd($xmlfile);
instead, it echoes out like this:
Making me very confused that i cannot get an object to work with. Code i present have worked fine before.
It may be that the data your being provided ha changed format, but the data is still there and you can still use it. The main problem with using something like dd() is that it doesn't work well with SimpleXMLElements, it tends to have it's own idea of what you want to see of what data there is.
In this case the namespaces are the usual problem. But if you look at the following code you can see a quick way of getting the data from a specific namespace, which you can then easily access as normal. In this code I use ->children("srw", true) to say fetch all child elements that are in the namespace srw (the second argument indicates that this is the prefix and not the URL)...
$apiUrl = "http://sru.bibsys.no/search/biblio?version=1.2&operation=searchRetrieve&startRecord=1&maximumRecords=10&query=ibsen&recordSchema=marcxchange";
$filename = "bok.xml";
$xmlfile = file_get_contents($apiUrl);
file_put_contents($filename, $xmlfile); // xml file is saved.
$fileXml = simplexml_load_string($xmlfile);
foreach ( $fileXml->children("srw", true)->records->record as $record) {
echo "recordIdentifier=".$record->recordIdentifier.PHP_EOL;
}
This outputs...
recordIdentifier=792012771
recordIdentifier=941956423
recordIdentifier=941956466
recordIdentifier=950546232
recordIdentifier=802109055
recordIdentifier=910941041
recordIdentifier=940589451
recordIdentifier=951721941
recordIdentifier=080703852
recordIdentifier=011800283
As I'm not sure which data you want to retrieve as the title, I just wanted to show the idea of how to fetch data when you have a list of possibilities. In this example I'm using XPath to look in each <srw:record> element and find the <marc:datafield tag="100"...> element and in that the <marc:subfield code="a"> element. This is done using //marc:datafield[#tag='100']/marc:subfield[#code='a']. You may need to adjust the #tag= bit to the datafield your after and the #code= to point to the subfield your after.
$fileXml = simplexml_load_string($xmlfile);
$fileXml->registerXPathNamespace("marc","info:lc/xmlns/marcxchange-v1");
foreach ( $fileXml->children("srw", true)->records->record as $record) {
echo "recordIdentifier=".$record->recordIdentifier.PHP_EOL;
$data = $record->xpath("//marc:datafield[#tag='100']/marc:subfield[#code='a']");
$subData=$data[0]->children("marc", true);
echo "Data=".(string)$data[0].PHP_EOL;
}
I followed the info here:
Parse XML namespaces with php SimpleXML
And that works for everything except the information contained in the "cap:geocode" and "cap:parameter" entries.
$geocode = $entry->children('cap',true)->geocode;
returns an empty value.
Any ideas on how to get at the data inside of the cap:geocode and cap:parameter entries?
<cap:geocode>
<valueName>FIPS6</valueName>
<value>048017 048079</value>
<valueName>UGC</valueName>
<value>TXZ027 TXZ033</value>
</cap:geocode>
I need to read the ValueName/Value pairs.
I used this example here: https://github.com/tylerlane/php.news-leader.com/blob/master/weather/alerts.php
And simplified it for my purposes to get this (echo.php just prints the data out):
$dataFileName = "wx/CAP.xml";
//load the feed
$capXML = simplexml_load_file($dataFileName);
//how many items
$itemsTotal = count($capXML->entry);
if(count($itemsTotal)):
$capXML->registerXPathNamespace('prefix', 'http://www.w3.org/2005/Atom');
$result = $capXML->xpath("//prefix:entry");
foreach($result as $capXML):
$dc = $capXML->children('urn:oasis:names:tc:emergency:cap:1.1');
$event = $dc->event;
$effective = $dc->effective;
$expires = $dc->expires;
$status = $dc->status;
$msgType = $dc->msgType;
$category = $dc->category;
$urgency = $dc->urgency;
$severity = $dc->severity;
$certainty = $dc->certainty;
$areadesc = $dc->areaDesc;
$geopolygon = $dc->polygon;
//get the children of the geocode element
$geocodechildren = $dc->geocode->children();
//only interested in FIPS6 for now
//no guarantee that FIPS6 will be the first child so we have to deal with that
if($geocodechildren->valueName == "FIPS6"){
//isolate all the FIPS codes
$fips = explode( " ", $geocodechildren->value );
} else {
//hide everything else so we don't fail
$fips = Array();
}
//get the VTEC
$parameter_children = $dc->parameter->children();
if($parameter_children->valueName == "VTEC"){
//isolate all VTEC codes
$vtec = explode( ".", $parameter_children->value );
} else {
//hide anything else that may show up
$vtec = Array();
}
include('echo.php');
print_r($fips);
echo "<br/>";
print_r($vtec);
echo "<hr/>";
endforeach;
endif;
Any ideas on how to get at the data inside of the cap:geocode and cap:parameter entries?
The key point in your case is, that the XML provided in the other question is invalid.
You would have noticed that if you had followed a good practice in PHP development: Enable reporting of errors, warning and notices to the highest level, display those as well as log those to file. Then track those warnings.
In your case you should have seen some message like:
Warning: simplexml...: namespace error : Namespace prefix cap on event is not defined in /path/to/script.php on line 42
This is a notice that the cap XML namespace prefix is undefined. That means that SimpleXML will drop it. Those elements are then put into the default namespace of the document so that you can access them directly.
So first of all make yourself comfortable with setting your php.ini file on your development system for development error reporting so that you'll be noticed about unexpected input values. One stop for that is the following question:
How to get useful error messages in PHP?
Next to that you need to decide why the input is wrong and how you'd like to deal with errors. Should it fail (the design of XML suggest to go with the fail route which is also considered a design issue for XML) or do you want to "repair" the XML or do you want to work with the invalid XML. That decision is up to. SimpleXML does work as announced, it's just in your case you got the error unnoticed and you're not doing any error handling so far.
The same problem with similar XML has been asked/answered about previously:
SimpleXML PHP Parsing [Duplicate] (marked as duplicate, albeit the duplicate does not talk about the error)
Create a WS-Security header using SimpleXML? ( create an XML document with SimpleXML with namespace prefixes)
I have a problem. My PHP script not working and I dont know why. This is XML file:
http://itoffice.sk/toto.xml
And this is my PHP code for read XML:
$xml = simplexml_load_file("toto.xml") or die("feed sa nepodarilo načítať");
$id = $xml->Zbozi['kod_zbozi'];
echo $id;
This not working. I need show and write into MySQL all attributes of <Zbozi>
<Zbozi> example:
<Zbozi diffgr:id="Zbozi1" msdata:rowOrder="0" kod_zbozi="DL139A" popis="HP DMS-59 to Dual DVI Cable Kit,accessory" part_number="DL139A" zaruka="24" dostupnost="false" dph="20" cena="29.23" cenaEU="35.34" snimek="DL139A/06_s.jpg" kod_vyrobce="093-001453" kod_kategorie="PC STOLNÍ POČÍTAČE" kategorie="Stolné počítače" kod_podkategorie="PŘÍSLUŠENSTVÍ ZN PC" podkategorie="Príslušenstvo značkových PC" typ="0" aut_poplatek="0.000" rema="0.000" sam_neprodejne="0" sn_vydej="false" ean="808736662901" doprodej="false">
Help me please. Thanks.
The reason your script was not working, is because you weren't following the XML tree properly. As you can see, your XML's structure looks like this:
<diffgr:diffgram>
<Cenik>
<Zbozi></Zbozi>
...
<Zbozi></Zbozi>
</Cenik>
</diffgr:diffgram>
Therefore you would have to follow that tree, and then loop for the Zbozi elements in order to get all of their attributes.
Take a look at this code:
$xml = simplexml_load_file('http://itoffice.sk/toto.xml');
$zbozi = $xml->children('diffgr', TRUE)->diffgram->children()->Cenik->Zbozi;
$id_arr = array();
foreach ($zbozi as $zbozi_element)
{
$id_arr[] = (string) $zbozi_element['kod_zbozi'];
}
First we load the XML file, nothing new there. But in the second line, we are traversing down the XML file. First we set the prefix namespace to diffgr (because of the semicolon in diffgr:diffgram), then we simply find the Zbozi elements, and loop for them.
You can see that in the loop I've added all of the kod_zbozi attribute values into an array. You can really do anything you'd like with that information at this point, this was just to get you started.
I am trying to search for a php variable in an xml file using xpath, but failing miserably. It works with a hard coded value, so I am nearly there..
<visitors>
<detail>
<id>876867</id>
<name>Bob McHaggis</name>
<email>bob#gmail.com</email>
</detail>
<detail>
<id>897987</id>
<name>Mark McBob</name>
<email>mark#gmail.com</email>
</detail>
</visitors>
<?php $sxe = simplexml_load_file("/CaptivePortal/visitors.xml");
foreach($sxe->xpath('//visitors/detail') as $item){
$row = simplexml_load_string($item->asXML());
$v = $row->xpath('//id[. ="'.$_COOKIE["judsons"].'"]');
} echo $v[0]; ?>
This works great checks for an id against the id stored in the cookie. But based on that value being found how do I access the name and email for the key matched?
Found & matched: 897987
I want to echo the name and email to, so based on that is Mark McBob & mark#gmail.com
My initial advice would be to take a good few minutes to (re-)read through the SimpleXML Basic Usage page in the PHP manual.
For selecting a single item within the XML file, there is no need at all to loop over anything. The example code below, and why it is different from yours, should become clear after familiarising yourself with the page mentioned above.
<?php
$search_id = (int) $_COOKIE["judsons"];
$visitors = simplexml_load_file("/CaptivePortal/visitors.xml");
$details = $visitors->xpath("detail[id='$search_id']");
if (empty($details)) {
echo "No details found for '$search_id'.";
} else {
$detail = $details[0];
$name = (string) $detail->name;
$email = (string) $detail->email;
echo "$name's email is $email";
}
The idea above is that $details will be an array containing hopefully just one <detail> element. It could be an empty array if no <detail> was found with the specified <id>, which is what the if(empty(…)) checks for. If the $details array is not empty, we're really only interested in the first one ($details[0]).
To access the information available within the <detail>, as explained on the "basic usage" page, the elements can be accessed with normal object property syntax ($detail->name). Doing so returns an object for that item (e.g. the <name>), so to get at the value as a string the object is type cast using (string).
You're actually doing quite a bit you don't need to there. For one thing, you don't need the loop. For another, inside your loop, you convert the context XML into a string (::asXML()), then convert it back into XML (simplexml_load_string()).
All you need is:
$xml = "<visitors><detail><id>876867</id><name>Bob McHaggis</name><email>bob#gmail.com</email></detail><detail><id>897987</id><name>Mark McBob</name><email>mark#gmail.com</email></detail></visitors>";
$sxe = simplexml_load_string($xml);
$row = $sxe->xpath('detail[id = '.$_COOKIE["judsons"].']');
That gets you the row. To extract part of it:
$name = $row[0]->xpath('name');
echo $name[0]; //Mark McBob
I'm trying to loop through multiple <LineItemInfo> products contained within a <LineItems> within XML I'm parsing to pull product Ids out and send emails and do other actions for each product.
The problem is that it's not returning anything. I've verified that the XML data is valid and it does contain the necessary components.
$itemListObject = $orderXML->getElementsByTagName('LineItemInfo');
var_dump($itemListObject->length);
var_dump($itemListObject);
The output of the var_dump is:
int(0)
object(DOMNodeList)#22 (0) {
}
This is my first time messing with this and it's taken me a couple of hours but I can't figure it out. Any advice would be awesome.
EDIT:
My XML looks like this... except with a lot more tags than just ProductId
<LineItems>
<LineItemInfo>
<ProductId href='[URL_TO_PRODUCT_XML]'>149593</ProductId>
</LineItemInfo>
<LineItemInfo>
<ProductId href='[URL_TO_PRODUCT_XML]'>149593</ProductId>
</LineItemInfo>
</LineItems>
Executing the following code does NOT get me the ProductId
$itemListObject = $orderXML->getElementsByTagName('LineItemInfo');
foreach ($itemListObject as $element) {
$product = $element->getElementsByTagName('ProductId');
$productId = $product->item(0)->nodeValue;
echo $productId.'-';
}
EDIT #2
As a side note, calling
$element->item(0)->nodeValue
on $element instead of $product caused my script's execution to discontinue and not throwing any errors that were logged by the server. It's a pain to debug when you have to run a credit card to find out whether it's functioning or not.
DOMDocument stuff can be tricky to get a handle on, because functions such as print_r() and var_dump() don't necessarily perform the same as they would on normal arrays and objects (see this comment in the manual).
You have to use various functions and properties of the document nodes to pull out the data. For instance, if you had the following XML:
<LineItemInfo attr1="hi">This is a line item.</LineItemInfo>
You could output various parts of that using:
$itemListObjects = $orderXML->getElementsByTagName('LineItemInfo');
foreach($itemListObjects as $node) {
echo $node->nodeValue; //echos "This is a line item."
echo $node->attributes->getNamedItem('attr1')->nodeValue; //echos "hi"
}
If you had a nested structure, you can follow basically the same procedure using the childNodes property. For example, if you had this:
<LineItemInfo attr1="hi">
<LineItem>Line 1</LineItem>
<LineItem>Line 2</LineItem>
</LineItemInfo>
You might do something like this:
$itemListObjects = $orderXML->getElementsByTagName('LineItemInfo');
foreach($itemListObjects as $node) {
if ($node->hasChildNodes()) {
foreach($node->childNodes as $c) {
echo $c->nodeValue .",";
}
}
}
//you'll get output of "Line 1,Line 2,"
Hope that helps.
EDIT for specific code and XML
I ran the following code in a test script, and it seemed to work for me. Can you be more specific about what's not working? I used your code exactly, except for the first two lines that create the document. Are you using loadXML() over loadHTML()? Are there any errors?
$orderXML = new DOMDocument();
$orderXML->loadXML("
<LineItems>
<LineItemInfo>
<ProductId href='[URL_TO_PRODUCT_XML]'>149593</ProductId>
</LineItemInfo>
<LineItemInfo>
<ProductId href='[URL_TO_PRODUCT_XML]'>149593</ProductId>
</LineItemInfo>
</LineItems>
");
$itemListObject = $orderXML->getElementsByTagName('LineItemInfo');
foreach ($itemListObject as $element) {
$product = $element->getElementsByTagName('ProductId');
$productId = $product->item(0)->nodeValue;
echo $productId.'-';
}
//outputs "149593-149595-"
XML tags tend to be lower-camel-case (or just "camel-case"), i.e. "lineItemInfo", instead of "LineItemInfo" and XML is case-sensitive, so check for that.