PHP XML Read Multi Nodes - php

i have a xml like below. How can parse this? i don't know how i can do this?
OZELLIK and DEGER is diffrent sometimes 5 sometimes 10. Please help me.
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<STOKLAR>
<STOK>
<SKU>1234</SKU>
<OZELLIKLER>
<OZELLIK>Ekran Kartı Belleği </OZELLIK>
<DEGER>Paylaşımlı </DEGER>
</OZELLIKLER>
</STOK>
<STOK>
<SKU>1454</SKU>
<OZELLIKLER>
<OZELLIK>İşlemci Üreticisi </OZELLIK>
<DEGER>Intel </DEGER>
<OZELLIK>İşlemci Tipi </OZELLIK>
<DEGER>Intel Core i5 </DEGER>
</OZELLIKLER>
</STOK>
</STOKLAR>
</ROOT>

It isn't that difficult with DOM and Xpath expressions:
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// iterate STOK element nodes
foreach ($xpath->evaluate('/ROOT/STOKLAR/STOK') as $stok) {
// fetch first SKU child element node as string
var_dump($xpath->evaluate('string(SKU)', $stok));
// iterate OZELLIK element nodes in OZELLIKLER
foreach ($xpath->evaluate('OZELLIKLER/OZELLIK', $stok) as $ozellik) {
var_dump(
// content of current OZELLIK
$ozellik->textContent,
// first following sibling element node, if DEGER, as string
$xpath->evaluate('string((./following-sibling::*)[1][self::DEGER])', $ozellik)
);
}
}
Output:
string(4) "1234"
string(22) "Ekran Kartı Belleği "
string(14) "Paylaşımlı "
string(4) "1454"
string(21) "İşlemci Üreticisi "
string(6) "Intel "
string(15) "İşlemci Tipi "
string(14) "Intel Core i5 "
DOMXpath::evaluate() can return a node list or a scalar value depending on the expression. The second argument sets the context node for the expression. Here is an explanation of the last (most complex) expression:
Get the following sibling element nodesfollowing-sibling::*
Limit to the first found node(following-sibling::*)[1]
Filter by node name DEGER(following-sibling::*)[1][self::DEGER]
Return text content of this node, empty string if no node was foundstring((following-sibling::*)[1][self::DEGER])
By default expressions work on the "child" axis. The expression uses two other axes "following-sibling" and "self" to look for the required nodes.

Related

How to correctly write a DOMXPath query for this XML?

I'm writing a script to parse for this XML.
I want to parse all the <Contents> node with DOMDocument and DOMXpath. But for some reason, all the XPath queries I tried failed.
My code:
<?php
$apiUrl = 'https://chromedriver.storage.googleapis.com/?delimiter=/&prefix=98.0.4758.48/';
$xmlContents = file_get_contents($apiUrl);
if (!$xmlDom->loadXML($xmlContents)) {
throw new \Exception('Unable to parse the chromedriver file index API response as XML.');
}
$xpath = new \DOMXPath($xmlDom);
// **I tried several $query values here**
$fileEntries = $xpath->query($query, null, false);
if (!$fileEntries instanceof \DOMNodeList) {
throw new \Exception('Failed to evaulate the xpath into node list.');
}
echo "There are {$fileEntries->length} results\n";
foreach ($fileEntries as $node) {
/** #var \DOMNode $node */
var_dump($node->nodeName);
}
XPath $query I tried:
/ListBucketResult/Contents
/Contents
//Contents
All of these results in "There are 0 results".
If I use * in the $query, it will list all the nodes within the <ListBucketResult> root node:
There are 10 results
string(4) "Name"
string(6) "Prefix"
string(6) "Marker"
string(9) "Delimiter"
string(11) "IsTruncated"
string(8) "Contents"
string(8) "Contents"
string(8) "Contents"
string(8) "Contents"
string(8) "Contents"
The easy way is to filter the nodes with the nodeName attribute. But I do want to know what went wrong with my XPath query. What did I miss?
What you missed - because you didn't see it in the view given - is, that all nodes are in a namespace, because the root element really is
<ListBucketResult xmlns="http://doc.s3.amazonaws.com/2006-03-01">
So this element and all of its children are in the namespace http://doc.s3.amazonaws.com/2006-03-01. Adding a namespace like this
$xpath->registerNamespace("aws", "http://doc.s3.amazonaws.com/2006-03-01");
after $xpath = new DOMXPath($xmlDom); and using it in your XPath expressions like that
/aws:ListBucketResult/aws:Contents
should solve your problem.

PHP simpleXML get value of different nodes depending on value

I have the following XML code:
<administration>
<notes>
<note>
<id>12312312</id>
<name>Lorem Ipsum</name>
<reference>Target Value - 1</reference>
</note>
<note>
<id>12312365</id>
<name>Lorem Ipsum</name>
<references>
<code>Dolor it se met.</code>
<code>Target Value - 2</code>
</references>
</note>
<note>
<id>12375512</id>
<name>Target Value - 3</name>
<reference>S</reference>
</note>
</notes>
<accounting>
<ledgers>
<ledger>
<debits>
<debit>
<description>Target Value - 4</description>
<amount>5467.32</amount>
</debit>
<debit>
<description>My Debit</description>
<amount>5467.32</amount>
<tags>
<tag>Target Value - 5</tag>
</tags>
</debit>
</debits>
<credits>
<credit>
<title>Target Value - 6</title>
<amount>873.00</amount>
</credit>
<credit>
<description>Target Value - 7</description>
<amount>23454.12</amount>
</credit>
</credits>
</ledger>
</ledgers>
</accounting>
</administration>
I'm trying to get a PHP array which consists of only the values of the nodes which have a value containing this string: "Target Value".
This has to be done on a recursive way, using an XML parser (I'm trying SimpleXML, but I'm new to that).
Up 'till now, I've been trying to use SimpleXmlIterator and foreach- and for-loops to achieve this, but I can't seem to check if a node value contains "Target Value".
Edit: reaching the target nodes by manually referring to them is not what I'm looking for, if I were, there would be no problem
Is there any way to achieve this?
EDIT:
Here is the code of my last try:
function sxiToArray($sxi)
{
$a = array();
for( $sxi->rewind(); $sxi->valid(); $sxi->next() )
{
if(!array_key_exists($sxi->key(), $a))
{
$a[$sxi->key()] = array();
}
if($sxi->hasChildren())
{
if (strpos((string)$sxi->current(), "Target Value"))
$a[$sxi->key()][] = sxiToArray($sxi->current());
}
else
{
if (strpos((string)$sxi->current(), "Target Value"))
$a[$sxi->key()][] = strval($sxi->current());
}
}
return $a;
}
$xmlArray = xml2array('../Document.xml');
print_r($xmlArray);
This gives the following result after running:
Array ( [notes] => Array ( ) [accounting] => Array ( ) )
It does not have to be done in an recursive way. You can use Xpath. Xpath uses location paths as part of an expression. The paths use different axes - one of them is descendant. It "ignores" the nesting. Xpath allows you to use conditions.
Get any element node in the document
//*
That has a text node as an child
//*[./text()]
with the text node containing the string "Target Value"
//*[./text()[contains(., "Target Value")]]
Put together it is a fairly small piece of code:
$administration = new SimpleXMLElement($xml);
$nodes = $administration->xpath('//*[./text()[contains(., "Target Value")]]');
foreach ($nodes as $node) {
var_dump($node->getName(), (string)$node);
}
Output:
string(9) "reference"
string(16) "Target Value - 1"
string(4) "code"
string(16) "Target Value - 2"
string(4) "name"
string(16) "Target Value - 3"
string(11) "description"
string(16) "Target Value - 4"
string(3) "tag"
string(16) "Target Value - 5"
string(5) "title"
string(16) "Target Value - 6"
string(11) "description"
string(16) "Target Value - 7"
And with DOM it would not look much different:
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$nodes = $xpath->evaluate('//*[./text()[contains(., "Target Value")]]');
foreach ($nodes as $node) {
var_dump($node->localName, $node->textContent);
}
Why don't you try str_pos() for "Target value"? I don't exactly know how you iterate through the XML but you could do something like:
if(str_pos($node, "Target value"){
//do whatever
}
That will tell you if any of the nodes at least contain that specific string.

Extract Data off multiple XML row

I have a really weird XML response and i need to extract it's data. I need to get the data in the "value" attribute but i need to choose them according to their "key" attributes.
This is how it looks like
<phone>
2125556666
</phone>
<State>
ny
</State>
<Response>
<data key="Supported" value="Yes"/>
<data key="Host" value="Remote"/>
<data key="WholeProductList">
<data key="Product" value="a-z44"/>
<data key="Product" value="c-k99"/>
<data key="Product" value="e-b089"/>
<data key="Product" value="z-p00"/>
<data key="Product" value="r-333"/>
<data key="Product" value="t-RS232"/>
<data key="Product" value="4-lve"/>
<data key="Product" value="Shutdown"/>
</data>
</Response>
In PHP i currenty have
$xmltmp = new DomDocument;
$xmltmp->loadXml($response);
$phone = $xmlresponse->getElementsByTagName('phone')->item(0)->nodeValue;
$state = $xmlresponse->getElementsByTagName('state')->item(0)->nodeValue;
echo $phone;
echo $state;
This currently outputs both phone number and state. It works fine.
Now i need to know if the "Supported" key's value is Yes or No, and if it's Yes, i need to get all "Products". I'm kinda stuck because i am having a hard time making the foreach statement and then checking the "key" attribute value.
Thanks!
Your XML is invalid. An XML document always needs a single document element node.
Example:
<root>
<phone>2125556666</phone>
<State>ny</State>
<Response>
<data key="Supported" value="Yes"/>
...
</data>
</Response>
</root>
The easiest way to fetch data from a DOM is XPath. In PHP that is provided by the DOMXPath class and part of the ext/dom. DOMXPath::evaluate() allows you to fetch node lists or scalar values from the DOM document.
$dom = new DOMDocument;
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$phone = $xpath->evaluate('string(/*/phone)');
$state = $xpath->evaluate('string(/*/State)');
var_dump($phone, $state);
Output:
string(10) "2125556666"
string(2) "ny"
An expression like /*/phone selects all phone element child nodes inside the document element. string(/*/phone) casts the first found node into a string and return that. If no node was found, it will return an empty string.
The XPath expression for the supported status is slightly more complex. Conditions for nodes are provided in []. It is possible to compare the result directly in XPath. The return value will be an boolean.
$supported = $xpath->evaluate('/*/Response/data[#key="Supported"]/#value = "Yes"');
var_dump($supported);
Output:
bool(true)
If the expression returns a node list you can iterate it with foreach().
$nodes = $xpath->evaluate(
'/*/Response/data[#key="WholeProductList"]/data[#key="Product"]/#value'
);
$products = [];
foreach ($nodes as $attributeNode) {
$products[] = $attributeNode->value;
}
var_dump($products);
Output:
array(8) {
[0]=>
string(5) "a-z44"
[1]=>
string(5) "c-k99"
[2]=>
string(6) "e-b089"
[3]=>
string(5) "z-p00"
[4]=>
string(5) "r-333"
[5]=>
string(7) "t-RS232"
[6]=>
string(5) "4-lve"
[7]=>
string(8) "Shutdown"
}
This won't quite work "as is" since I don't know what the actual structure of the XML document is, but in short you map the XML nodes to XPath like //root/node/child_node/#attribute and so on.
It should also have some sanity (not null) type checking in.
$xmltmp = new DomDocument;
$xmltmp->loadXml($response);
$xQuery = new DOMXPath($xmltmp);
//not sure what your root node is so the query path is probably wrong
$supported = $xQuery->query('/Response/data[#key="Supported"]/#value')->value;
You can also replace:
$phone = $xmlresponse->getElementsByTagName('phone')->item(0)->nodeValue;
$state = $xmlresponse->getElementsByTagName('state')->item(0)->nodeValue;
With something like (again - without the full structure of the XML document the path itself is probably not quite right):
$phone = $xQuery->query('/phone')->item(0)->nodeValue;
$state = $xQuery->query('/State')->item(0)->nodeValue;

var_dump for DOMNodeList printout unexpected result

I have a xml file I load it as the following:
//$file the file system path of the xml file
function getTopicsList($file){
$doc = new DOMDocument();
$doc->load( $file );
var_dump($doc->getElementsByTagName('topic'));
return $doc->getElementsByTagName('topic');
}
The loaded xml file contents is something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<topics>
<topic>
<title>Title1</title>
<keywords>"Some Keys"</keywords>
</topic>
<topic>
<title>The Title</title>
<keywords>Another Key</keywords>
</topic>
<topic>
<title>A Title</title>
<keywords>Key two</keywords>
</topic>
</topics>
The var_dump() in the above code just printout limited information such as:
object(DOMNodeList)#30 (1) {
["length"]=>
int(3)
}
I expected that it should print at least the properties of that object i.e the xml tags and its values. I tried to use other functions such as print_r() and var_export() but there is no details I want.
No, this is node list. You can iterate it with foreach or access nodes using the item() method.
Node lists are used at different places, getElementsByTagName() is one, another is the $childNodes property. Xpath expressions return node lists, too.
Be aware that the nodes can be not only elements but several node types. Like text, cdata section or attribute.
You can use var_dump() to dump a single node. This works with PHP >= 5.3.11 or >= 5.4.1.
$dom = new DOMDocument();
$dom->loadXML('<foo/>');
var_dump($dom->documentElement);
Output:
object(DOMElement)#2 (18) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
string(3) "foo"
["textContent"]=>
string(0) ""
["baseURI"]=>
string(1) "/"
["localName"]=>
string(3) "foo"
["prefix"]=>
string(0) ""
["ownerDocument"]=>
...

get the last child node from xml using DOMDocument in php

Here is my xml:
<details>
<car>
<id>61XZB6</id>
<Jan-01-14>20</Jan-01-14>
<Jan-02-14>435</Jan-02-14>
<Jan-03-14>454</Jan-03-14>
<Jan-04-14>768</Jan-04-14>
<Jan-05-14>24</Jan-05-14>
<Jan-06-14>675</Jan-06-14>
<Jan-07-14>213</Jan-07-14>
<Jan-08-14>44</Jan-08-14>
<Jan-09-14>565</Jan-09-14>
<Jan-10-14>80</Jan-10-14>
<Jan-11-14>998</Jan-11-14>
<Jan-12-14>67</Jan-12-14>
<Jan-13-14>77</Jan-13-14>
<Jan-14-14>909</Jan-14-14>
<Jan-15-14>34</Jan-15-14>
<Jan-16-14>887</Jan-16-14>
<Jan-17-14>767</Jan-17-14>
<Jan-18-14>545</Jan-18-14>
<Jan-19-14>67</Jan-19-14>
<Jan-20-14>787</Jan-20-14>
<Jan-21-14>898</Jan-21-14>
<Jan-22-14>435</Jan-22-14>
<Jan-23-14>42</Jan-23-14>
<Jan-24-14>232</Jan-24-14>
<Jan-25-14>234</Jan-25-14>
<Jan-26-14>675</Jan-26-14>
<Jan-27-14>46</Jan-27-14>
<Jan-28-14>546</Jan-28-14>
<Jan-29-14>88</Jan-29-14>
<Jan-30-14>0</Jan-30-14>
<Jan-31-14>0</Jan-31-14>
</car>
</details>
My query is how to check the last node inside each tag before inserting a new node to each tag.Thanks in advance for any sort of help extended.
If I understand you correctly you would like to check for the last element in each car element node? Well, Xpath hast two methods position() and last() that can be used in a condition.
Select the car nodes
/details/car
Select the child element nodes of the car nodes
/details/car/*
Add a condition to limit the selection to the last node
/details/car/*[last()]
Full example: https://eval.in/145531
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
foreach ($xpath->evaluate('/details/car/*[last()]') as $node) {
var_dump(
$node->nodeName,
$node->nodeValue
);
}
Output:
string(9) "Jan-31-14"
string(1) "0"
HINT!
Flexible element names are really bad style, you will not be able to define them in a schema. If possible I suggest you change them to something like <amount date="Jan-31-14">0</amount>

Categories