I'm parsing XML from a data source we use in our web application and I'm having some trouble accessing data from a specific part in the XML.
First, here's the output of a print_r on what I'm trying to access.
SimpleXMLElement Object
(
[0] =>
This is the value I'm trying to get
)
Then, here's the XML I'm trying to get.
<entry>
<activity:object>
<activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
<id>542</id>
<title>
Title string is a string
</title>
<content>
This is the value I'm trying to get
</content>
<link rel="alternate" type="html" href="#"/>
<link rel="via" type="text/html" href="#"/>
</activity:object>
</entry>
The content element is what I'm after.
When I access it with $post->xpath('activity:object')[0]->content I end up with what's above.
I've tried using $zero = 0; as well as ->content->{'0'} to access this element, but each time I just get an empty SimpleXML object returned, like below.
SimpleXMLElement Object
(
)
Is there another way to access this that I haven't found yet?
Thanks!
xpath returns a simpleXMLElement type, which has a function to convert it into a string. Try this function:
http://php.net/manual/en/simplexmlelement.tostring.php
You should just be able to access it directly:
$content = $post->xpath('//content');
echo $content[0];
With PHP 5.4 or higher you might be able to do do this:
$content = $post->xpath('//content')[0];
Or if you convert the XML to string like #kkhugs says you can use
/**
* substr_delimeters
*
* a quickly written, untested function to do some string manipulation for
* not further dedicated and unspecified things, especially abused for use
* with XML and from http://stackoverflow.com/a/27487534/367456
*
* #param string $string
* #param string $delimeterLeft
* #param string $delimeterRight
*
* #return bool|string
*/
function substr_delimeters($string, $delimeterLeft, $delimeterRight)
{
if (empty($string) || empty($delimeterLeft) || empty($delimeterRight)) {
return false;
}
$posLeft = stripos($string, $delimeterLeft);
if ($posLeft === false) {
return false;
}
$posLeft += strlen($delimeterLeft);
$posRight = stripos($string, $delimeterRight, $posLeft + 1);
if ($posRight === false) {
return false;
}
return substr($string, $posLeft, $posRight - $posLeft);
}
$content = substr_delimeters($xmlString, "<content>", "</content>");
print_r is always misleading regarding SimpleXMLElement. The output you have for example:
Code Reference:
$testate = $post->xpath('activity:object')[0]->content;
print_r($testate);
Output:
SimpleXMLElement Object
(
[0] =>
This is the value I'm trying to get
)
It does not mean that you would need to access the text you're looking for by using array index zero ([0]). Well actually, while it doesn't mean that, it does not mean that it's not possible. Confusing, I know.
However, you're looking for a string value, not the object (value). All you need to do here is to cast to string:
$testate = $post->xpath('activity:object')[0]->content;
$text = (string) $testate;
########
The important part here is really the casting to string. Like print_r already suggested to you, using the zero index would work, too:
$text = (string) $testate[0];
But the zero-index is just not necessary and only internal information.
Just important to keep with you: Don't rely to print_r with SimpleXMLElement. It is an object and print_r merely tells you here that it's one and which name it has (which object type it is), the rest it outputs within the curly brackets is internal information of that object. It's never the whole picture of the XML you have. Even if it first seems so.
Read more about that here: php SimpleXML attributes are missing
So just big warning here, keep that in mind. Consider casting to string (or use with string functions like trim()) and you're fine.
Also do not forget to read through Basic SimpleXML usage in the PHP manual.
P.S.: As the other answer shows, you're not the only one having problems to describe this magic nature of SimpleXML.
P.P.S.: you might want to learn about XML-Namespaces soon, that is when elements names have colons (you perhaps already did, I can not see it for sure from your code).
Related
I currently have this large JSON file: hastebin
But just want the titles of the posts.
I've tried this...
$json = $page;
$o = json_decode($json, true);
echo($json);
$titles = $o["*"]["*"]["*"]["*"]["title"];
var_dump($titles);
But it isn't working - it's returning NULL! Sometimes it just doesn't return anything.
If anyone is wondering, yes this is from Reddit.
This should do it:
$titles = array_map(function($post) {
return $post['data']['title'];
}, $o['data']['children']);
I'm not sure what you expected using "x" indices, but you should probably read about arrays.
PHP can't use wildcards like * in array keys. Whatever string you use to reference the key, it's going to try to find a key with that exact string. So what you tried can't work because there aren't any * keys.
You can get it by iterating all the levels, or iterating the outer level and referring to the proper nested key. But if you're just looking for all instances of 'title' a recursive method may be an easier way to get them.
array_walk_recursive($o, function($value, $key) use (&$titles) {
if ($key == 'title') $result[] = $value;
});
var_dump($titles);
This will get any value of 'title' regardless of its depth in the array, so if that's not what you want, then you'll need to iterate it and specifically reference the proper ones.
It's very hard to deal directly with such a long JSON document. The returned result from the page is not a valid JSON. It contains some HTML tags, but if you take the posts data and insert it in a file you can do the following according to the structure of your JSON (You can find your JSON in an external link here):
<?php
header("Content-Type:application/json");
$posts=file_get_contents('json.php');
//decode your JSON STRING
$posts=json_decode($posts,true);
//create a title variable to store your titles
$titles=array();
foreach($posts['data']['children'] as $child)
{
array_push($titles,$child['data']['title']);
}
echo json_encode($titles);
?>
You can even use this approach using a URL but ensure that it will return a valid JSON with no html
I have the following XML:
<Root>
<personalData>
<userName>John Tom</userName>
<email>mail#example.com</email>
</personalData>
<profesionalData>
<job>engineer</job>
<jobId>16957</jobId>
</profesionalData>
</Root>
Doing in my debugger:
$myObject->xpath('//Root/profesionalData')
I have:
: array =
0: object(SimpleXMLElement) =
job: string = engineer
jobId: string = 16957
I cannot get hold of the jobId 16957.
What do I have to do?
$root = simplexml_load_file('file.xml');
$job_ids = $root->xpath('//profesionalData/jobId');
if (!$job_ids) {
die("Job IDs not found");
}
foreach ($job_ids as $id) {
// SimpleXmlElement implements __toString method, so
// you can fetch the vlaue by casting the object to string.
$id = (string)$id;
var_dump($id);
}
Sample Output
string(5) "16957"
Notes
You don't need to specify Root in the XPath expression, if you are going to fetch all profesionalData/jobId tags no matter where they are in the document, just use the double slash (//) expression. This approach may be convenient in cases, when you want to avoid registering the XML namespaces. Otherwise, you can use a strict expression like /Root/profesionalData/jobId (path from the root). By the way, your current expression (//Root/profesionalData/jobId) matches all occurrences of /Root/profesionalData/jobId in the document, e.g. /x/y/z/Root/profesionalData/jobId.
Since SimpleXmlElement::xpath function returns an array on success, or FALSE on failure, you should iterate the value with a loop, if it is a non-empty array.
SimpleXmlElement implements __toString method. The method is called when the object appears in a string context. In particular, you can cast the object to string in order to fetch string content of the node.
I have a stock report file (coming from an outer source, therefore I can't modify in any way) and I would like to iterate over all elements (I have to save them into a MySQL table). As I see the $xml->Stockfile is an array of objects (2 items), so I tried to put it into an array.
For some reason the $myarray contains only the first element after the $myarray = $xml->StockFile assignment.
here is my code:
$xml = simplexml_load_file("../docs/stock.xml");
print_r($xml);
$myarray = $xml->StockFile;
print_r($myarray);
stock.xml:
<NewDataSet>
<StockFile>
<MatrixID>1533</MatrixID>
<Brand>myBrand</Brand>
<ProductCode>001</ProductCode>
<RRP>29.99</RRP>
<Image2Name />
<Image3Name />
</StockFile>
<StockFile>
<MatrixID>1534</MatrixID>
<Brand>myBrand</Brand>
<ProductCode>002</ProductCode>
<RRP>29.99</RRP>
<Image2Name />
<Image3Name />
</StockFile>
</NewDataSet>
Why I'm getting only one item instead of all?
What should I do do retrieve the whole array?
Take care with SimpleXMLElement. It has a lot of magic. Know the magic or get puzzled by print_r or var_dump or similar output. Your example extended:
$myarray = $xml->StockFile;
print_r($myarray); # shows one element
# foreach has both elements:
foreach($myarray as $name => $stockfile)
{
echo $name, ":\n", $stockfile->asXML(), "\n\n";
}
Even though it is the same variable ($myarray) it behaves differently depending on context it is used in. Inside a foreach the SimpleXMLElement (that is the type of that object) will provide an iterator over the child-elements named StockFile as specified here:
$myarray = $xml->StockFile;
However using that variable in some kind of single context, it will for example return the inner string of the first child-element with that name:
echo $myarray, "\n";
(which in your case is just some lines of whitespace).
See Demo: https://eval.in/83787
Running into this "trap" by SimpleXML is actually pretty common. I suggest to understand the basic usage by the example given in the manual:
Basic SimpleXML usage
change the last two lines to
foreach ($xml->StockFile as $nextStockFile) {
print_r ($nextStockFile);
}
I want to get polygon coordinates from below String.
{"polygon":{"type":"Feature","properties":[],"geometry":{"type":"Polygon","coordinates":[[[-7302732.4720101,6527844.6333235],[-3193477.8319711,6606116.1502766],[-5111129.9973226,5001550.0527375],[-6637424.5779086,4884142.7773079],[-7772361.5737289,5158093.0866438],[-7302732.4720101,6527844.6333235]]]},"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}}
This is GeoJson string that i decode to array with below code:
$polygon = CJSON::decode($str);
when i want to get polygon i get error!
$var= $polygon->polygon;
or with below code:
$polygon = CJSON::decode($str,true);
$var = $polygon['polygon'];
although for getting coordinates:
foreach($polygon as $key=>$value)
$coordinates = $value['coordinates'];
or
$coordinates = $value[coordinates];
how can i get coordinates from geojson that i send from javascript to php for saving on postgresql with postgis?
$polygon->polygon->geometry->coordinates[0]
or
$polygon['polygon']['geometry']['coordinates'][0]
what you have is a multidimensional array/object not sure which its being output to when decoded in your case as it appears you have a class doing it I would have just used json_decode, but anyway. Yea from the looks of it, polygon is the main object, then in it is geometry which is an object that has type and coordinates, and then coordinates has multiple objects/arrays in it.
the above samples if I typed them right will show the first set of coordinates in that object. Of course you could run it through a loop ie:
In the case that it is an object assuming your Class decodes as an object and not an array. Not exactly sure what $polygon = CJSON::decode($str,true); does. But if its anything like json_decode() then you should be all set.
This is my method of breaking down the object as you present here, its worth noting you may want to check counts, and see if the object is set first, or if the property exists in the object to prevent other means of the code breaking down the road. But what I have here is just pure example at its core, it will server its purpose though. But will not error handle which is why I say you may want to elaborate further on it doing those checks.
Anyway heres my code:
<?php
$str = '{"polygon":{"type":"Feature","properties":[],"geometry":{"type":"Polygon","coordinates":[[[-7302732.4720101,6527844.6333235],[-3193477.8319711,6606116.1502766],[-5111129.9973226,5001550.0527375],[-6637424.5779086,4884142.7773079],[-7772361.5737289,5158093.0866438],[-7302732.4720101,6527844.6333235]]]},"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}}';
$polygon = json_decode($str);
echo'<pre>';print_r($polygon);echo'</pre>';
$set = 1;
foreach($polygon->polygon->geometry->coordinates[0] as $coordinates)
{
echo 'Set '.$set.': ';$set++;
echo $coordinates[0].','.$coordinates[1].'<br>';
}
?>
see it in action http://7pz.net/geojson-parse.php (scroll to the bottom)
This should give you an array of all the coordinates and print them out line by line:
$string = '{"polygon":{"type":"Feature","properties":[],"geometry":{"type":"Polygon","coordinates":[[[-7302732.4720101,6527844.6333235],[-3193477.8319711,6606116.1502766],[-5111129.9973226,5001550.0527375],[-6637424.5779086,4884142.7773079],[-7772361.5737289,5158093.0866438],[-7302732.4720101,6527844.6333235]]]},"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}}}}';
$json = json_decode($string);
$coords_array = $json->polygon->geometry->coordinates[0];
foreach($coords_array as $c_a) {
echo $c_a[0] . "," .$c_a[1] . "<br>";
}
Access with:
$coords_array[0];
$coords_array[1];
$coords_array[2];
etc.
Basically you can turn the JSON string into an object and access each element with the -> notation.
I usally use a site called http://jsonviewer.stack.hu/ to decode JSON and find the path I need, then simply write them out as they appear, as in as in the above - $json->polygon->geometry->coordinates;.
Try it out yourself on the site.
I'm attempting to use the VirusTotal API to return the virus scan for a certain file. I've been able to get my current PHP code to upload the file to VirusTotal as well as get the results in an array. My question is, how would I get the [detected] value from every virus scanner under the scans object? My PHP code is below as well as a link to the output of the array.
require_once('VirusTotalApiV2.php');
/* Initialize the VirusTotalApi class. */
$api = new VirusTotalAPIV2('');
if (!isset($_GET["hash"])) {
$result = $api->scanFile('file.exe');
$scanId = $api->getScanID($result);
$api->displayResult($result);
} else {
$report = $api->getFileReport($_GET["hash"]);
$api->displayResult($report);
print($api->getSubmissionDate($report) . '<br>');
print($api->getReportPermalink($report, TRUE) . '<br>');
}
http://joshua-ferrara.com/viruscan/VirusTotalApiV2Test.php?hash=46faf763525b75b408c927866923f4ac82a953d67efe80173848921609dc7a44
You would probably have to iterate each object under scans in a for loop and either store them in yet another array or echo them out of just want to print. For example
$detectedA = {nProtect, CAT-QuickHeal, McAfee...nth};
$datContainer = array();
for ($i = 0; i < $api.length ; i++){
//Either store in an array
$api->$scans->detectedA(i)-> detected = $datContainer(i);
//Or echo it all
echo $api->$scans->detectedA(i)->detected;
return true;
}
Granted that's probably not the way you access that object but the idea still applies.
This description of stdClass demonstrates how you can not only store arbitrary tuples of data in an object without defining a class, but also how you can cast an arbitrary object as an array - which would then let you iterate over the sub-objects in your case.
Or, if I've misunderstood your question and you're actually getting an array back from the VirusTotal API and not a stdClass instance, then all you need to do is loop.
Store the scans into an array (of scans), then just loop through the array as usual.
foreach($scans as $scan) echo $scan->detected;
Or, if I'm not quite understanding the question right, is detected an array (or an object)?
Edit because of your comments -
The object returned holds an object of objects, so you need to do some casting.
foreach((array)$scans as $scanObj) {
$scan=(array)$scanObj;
foreach($scan as $anti) {
print $anti->detected; } }