Getting multiple values with specific attributes in nested xml with php - php

I feel like this should be easy, but for the love of it I cannot get it right. I've read and tested for hours, trying different approaches I have found on the interwebs, both with simpleXML and PHP XML DOM, but none of them works just the way I need.
I got somewhat close, after tweaking and combining a few answers (that I cannot even find the way back to cause I've been through so many of them here), but not entirely correct.
My code:
$xml = simplexml_load_file(all_objects.xml");
$image = $xml->xpath('/objects/object/images/image');
foreach($image as $node) {
$id = (string) $node->field[0];
$url = (string) $node->field[4];
echo $id . " : " . $url . "<br>";
}
But instead of using the key/number they appear [4] in, I would like to target using the field name attribute, e.g. "id" and "image_url", as they are not always in this order.
Something like this:
$id = (string) $node->field["id"];
But it does not work, I tried with field->attribtues()->id too, but no luck.
The xml:
<objects>
<object>
<field name="id">1055</field>
<field name="title">example object</field>
<images>
<image number="1">
<field name="id">55</field>
<field name="version">1</field>
<field name="image_url_small">http://example.com/image-small.jpg</field>
<field name="image_url_medium">http://example.com/image-medium.jpg</field>
<field name="image_url_big">http://example.com/image-big.jpg</field>
<field name="image_url_original">http://example.com/image.jpg</field>
</image>
<image number="2">
<field name="id">56</field>
<field name="version">2</field>
<field name="image_url">http://example.com/image2.jpg</field>
</image>
<image number="3">...</image>
...
<image number="25">...</image>
</images>
</object>
<object>...</object>
<object>...</object>
</objects>
I would really appreciate any help/guidance! I am losing my mind over this.

You can use XPath to get child element with certain attribute value :
foreach($image as $node) {
$id = (string) $node->xpath('field[#name="id"]')[0];
$url = (string) $node->xpath('field[#name="image_url_big"]')[0];
echo $id . " : " . $url . "<br>";
}
eval.in demo
output :
55 : http://example.com/image-big.jpg
56 :
:
:

Related

Accessing a specific XML element using xpath in PHP 7.x

Ok so I have some XML data.
$mydata = <<<XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE fmresultset PUBLIC "-//FMI//DTD fmresultset//EN" "https://HIDDEN:443/fmi/xml/fmresultset.dtd">
<fmresultset xmlns="http://www.filemaker.com/xml/fmresultset" version="1.0">
<resultset count="1" fetch-size="1">
<record mod-id="27" record-id="754">
<field name="a_Constant_c">
<data>1</data>
</field>
<field name="a_Sch_ID_pk">
<data>100060</data>
</field>
<field name="a_SchoolHead_pk">
<data>100060_1</data>
</field>
<field name="b___Data_____________">
<data/>
</field>
<field name="b_1Name_School_Code_t">
<data>PJA</data>
</field>
<field name="b_1Name_School_t">
<data>Palmetto</data>
</field>
<field name="b_1Name_SchoolHead_t">
<data>John Doe</data>
</field>
<field name="b_Ad_Address1_t">
<data/>
</field>
<field name="b_Ad_Address2_t">
<data>123 Main St.</data>
</record>
</resultset>
</fmresultset>
XML;
Now what I want to do is basically be able to read the value of the data from a specific field and assign it to a variable.
So far I have something like this...
$xml = simplexml_load_string($mydata);
Now I want to be able to assign let's say the data in the field name b_1Name_School_Code_t (which is PJA)
So I think it should be something like this
$school = $xml->resultset->record->field->data;
echo "School Name: ".$school;
Then I would like to see on the screen
School Name: PJA
So what I am missing to be able to make this happen?
You are only getting to the first field element in your example, which is why you get 1. Instead, loop through all the field elements, and stop when you get to the one you need:
$xml = simplexml_load_string($mydata);
$fields = $xml->resultset->record->field;
foreach ($fields as $field) {
if ((string) $field->attributes()->name === "b_1Name_School_Code_t") {
echo "School name: ".$field->data; // School name: PJA
break;
}
}
Demo
I use SimpleXMLElement::attributes() to get the name attribute of the element (note the cast to string, otherwise you get an SimpleXMLElement)
However, it would make more sense to use XPath to go directly to the element you're after:
$xml = simplexml_load_string($mydata);
$xml->registerXPathNamespace("fmresultset", "http://www.filemaker.com/xml/fmresultset");
$node = $xml->xpath("//fmresultset:resultset/fmresultset:record/fmresultset:field[#name='b_1Name_School_Code_t']");
var_dump($node[0]->data); // PJA
Demo
Notice the namespace registration and the accessing of the first element, since xpath() returns an array of SimpleXMLElements

Parse XML value based on attribute

<ITEMS>
<ITEM ItemID="XY">
<ItemSearchName />
<ITEMDESCRIPTION>
<DESCRIPTION descriptionType="T" descriptionTypeTitle="Short" languageId="1" language="English">English description</DESCRIPTION>
<DESCRIPTION descriptionType="T" descriptionTypeTitle="Short" languageId="2" language="France">Fance description</DESCRIPTION>
</ITEMDESCRIPTION>
<ItemType>B</ItemType>
<ItemDepartment />
<ITEMDIMENSIONS>
<ItemDimensionUOM>m</ItemDimensionUOM>
</ITEMDIMENSIONS>
<ItemGrossWeihgt>0.00</ItemGrossWeihgt>
</ITEM>
</ITEMS>
This is an example of my XML file parsed with simplexml method. I do read ItemID attribute value with this code (just an example):
$item->attributes()->ItemID
My question is, how to access DESCRIPTION under DESCRIPTION attribute languageId = 1?
I can do this with foreach:
foreach ($item->ITEMDESCRIPTION->DESCRIPTION as $desc) {
if ($desc['languageId'] == '1') {
echo "<td>" . $desc . "</td>";
}
}
but I would prefer not to use foreach.
Another possibility is this:
$desc2 = $item->ITEMDESCRIPTION->DESCRIPTION[1];
but this is just the first DESCRIPTION and not the description with attribute languageId = 1.
Thanks for any suggestions!
You can also use xpath to get the particular value. Example:
$xml_string = '<ITEMS> <ITEM ItemID="XY"> <ItemSearchName /> <ITEMDESCRIPTION> <DESCRIPTION descriptionType="T" descriptionTypeTitle="Short" languageId="1" language="English">English description</DESCRIPTION> <DESCRIPTION descriptionType="T" descriptionTypeTitle="Short" languageId="2" language="France">Fance description</DESCRIPTION> </ITEMDESCRIPTION> <ItemType>B</ItemType> <ItemDepartment /> <ITEMDIMENSIONS> <ItemDimensionUOM>m</ItemDimensionUOM> </ITEMDIMENSIONS> <ItemGrossWeihgt>0.00</ItemGrossWeihgt> </ITEM></ITEMS>';
$xml = simplexml_load_string($xml_string);
$value = $xml->xpath('//ITEMDESCRIPTION/DESCRIPTION[#languageId="1"]')[0];
echo (string) $value; // English description
Or just simple foreach:
foreach($xml->ITEM->ITEMDESCRIPTION->DESCRIPTION as $desc) {
if($desc->attributes()['languageId'] == 1) {
echo (string) $desc; // English description
}
}

How can I access two XML elements with the same Name using SimpleXML?

I am trying to parse an XML which is some ocassions have elements with the same name tag. For example:
<Event Region="xxx" RegionName="xxx">
<Id>xxx</Id>
<Name>xxx</Name>
<Price Name="V" ProductMessage="" ServiceCharge="xx">xx</Price>
<Price Name="A" ProductMessage="" ServiceCharge="xx">xx</Price>
<URL>xxx</URL>
<Date>xxx</Date>
<GMTOffset>-x</GMTOffset>
<StartTime>xx</StartTime>
<EndTime>xxx</EndTime>
</event>
In this case Price is repeated, in some others there is just one Price.
Is there any way to retrieve the count of Price and access each individually?
Thanks for any tip!
Using SimpleXML
$xml = new SimpleXMLElement($data); //Where $data represents your XML string
foreach($xml->Price as $price) echo (string)$price . '<br>';
As documented, you can treat the repeated elements as array items:
$xml = simplexml_load_string('<Event Region="xxx" RegionName="xxx">
<Id>xxx</Id>
<Name>xxx</Name>
<Price Name="V" ProductMessage="" ServiceCharge="xx">a</Price>
<Price Name="A" ProductMessage="" ServiceCharge="xx">b</Price>
<URL>xxx</URL>
<Date>xxx</Date>
<GMTOffset>-x</GMTOffset>
<StartTime>xx</StartTime>
<EndTime>xxx</EndTime>
</Event>');
echo $xml->Price[0] . PHP_EOL;
echo $xml->Price[1] . PHP_EOL;
a
b
(I've fixed your data sample to make it valid XML.)

How to get the item attribute of an xml file, when the the number of items within a tag is always changing

I have this bit of xml code below and I'm trying to get the value of all of the "result value" attributes within the results tag. The thing is...this is going to be a live feed, so there may be 1,2 or 3 result items within that tag.
Do I need to do some sort of count to see how many items are within the results tag?
<Match ct="0" id="771597" LastPeriod="2 HF" LeagueCode="19984" LeagueSort="1" LeagueType="LEAGUE" startTime="15:00" status="2 HF" statustype="live" type="2" visible="1">
<Home id="11676" name="Manchester City" standing="1"/>
<Away id="10826" name="Newcastle United" standing="3"/>
<Results>
<Result id="1" name="CURRENT" value="1-1"/>
<Result id="2" name="FT" value="1-1"/>
<Result id="3" name="HT" value="1-0"/>
</Results>
<Information>
<league id="19984">Premier League</league>
<note/>
<bitarray/>
<timestamp/>
</Information>
</Match>
Thanks in advance
SimpleXML
Just loop through the results with SimpleXML to grab each value and name attribute, this will work with a variable number of results.
Demo
$obj = simplexml_load_string($xml);
foreach($obj->Results->Result as $result)
{
echo $result->attributes()->name . ': ' . $result->attributes()->value . "\n";
}
Outputs
CURRENT: 1-1
FT: 1-1
HT: 1-0
If you have a root node such as Matches with multiple Match under it then you would use a nested foreach like so:
foreach($obj->Match as $match)
{
foreach($match->Results->Result as $result)
{
echo $result->attributes()->name . ': ' . $result->attributes()->value . "\n";
}
}
DOMDocument
To do the same using DOMDocument instead of SimpleXML:
$dom = new DOMDocument();
$dom->loadXML($xml);
foreach($dom->getElementsByTagName('Match') as $match)
{
foreach($match->getElementsByTagName('Result') as $result)
{
echo $result->getAttribute('name') . ': ' . $result->getAttribute('value') . "\n";
}
}
Outputs the same as the SimpleXML method.

How can I make xpath queries dynamic?

I am working in XPATH using PHP. I have done it by hard coded. but I made a lot of googling for making xpath queries dynamic. Is there any solution for making xpath queries dynamic?
I have the following xml code:
<information>
<field name="secondtitle">ABCt</field>
<field name="author">XYZ</field>
<field name="ISBN">9780712617611</field>
<field name="publisher">abc</field>
</information>
the other file's data is in the following:
<information>
<field name="deliveryCosts">1.95</field>
<field name="deliveryTime">3 - 5 Werkdagen</field>
<field name="EAN">9789021142523</field>
<field name="ISBN">9789021142539</field>
<field name="subcategories">abc</field>
<field name="auteur">IJK</field>
<field name="type">xyz</field>
</information>
only the attributes are differ now m trying to access them in a single php file. but all of my queries are hard coded but i want to access them dynamically.
following is my php code that i made hard coded:
$auth = $xml->xpath("/products/product[$i]/additional/field[#name='auteur']");
$type = $xml->xpath("/products/product[$i]/additional/field[#name='type']");
foreach($auth as $au)
{
foreach($type as $ty)
{
echo $au = mysql_real_escape_string($au);
echo $ty = mysql_real_escape_string($ty);
}}
This code is in working with the second code of xml that I have paste above.
You can have a "skelleton" expression and make an "executable instance". I don't know PHP, but in C# this can be done like this:
string XpathExpression
= string.Format("/products/product[$i]/additional/field[#name='{0}']",
myName)
XPATH provides some operator hopefully you are using them. As | operator is user for taking union of two queries and also there is an operator for taking intersection of two or more queries.
Here in the following I'm suggesting you use:
$auth = $xml->xpath("/products/product[$i]/additional/field[#name='auteur' or #name='author']");
foreach($auth as $au)
{
foreach($type as $ty)
{
echo $au = mysql_real_escape_string($au);
}}
It's working here in case of the author, and for the problem of the type of the product follow my following code.
$type = $xml->xpath("/products/product[$i]/additional/field[#name='type']");
if(!empty($type ))
{
echo 'the node is available';
}
else
{
echo 'the node is not available';
}

Categories