I have an XML document with the following structure:
<posts>
<user id="1222334">
<post>
<message>hello</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>hello client how can I help?</message>
<client>operator</client>
<time>time</time>
</post>
</user>
<user id="2333343">
<post>
<message>good morning</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>good morning how can I help?</message>
<client>operator</client>
<time>time</time>
</post>
</user>
</posts>
I am able to create the parser and print out the whole document, the problem is however that I want to print only the (user) node and children with a specific attribute (id).
my PHP code is:
if( !empty($_GET['id']) ){
$id = $_GET['id'];
$parser=xml_parser_create();
function start($parser,$element_name,$element_attrs)
{
switch($element_name)
{
case "USER": echo "-- User --<br>";
break;
case "CLIENT": echo "Name: ";
break;
case "MESSAGE": echo "Message: ";
break;
case "TIME": echo "Time: ";
break;
case "POST": echo "--Post<br> ";
}
}
function stop($parser,$element_name){ echo "<br>"; }
function char($parser,$data){ echo $data; }
xml_set_element_handler($parser,"start","stop");
xml_set_character_data_handler($parser,"char");
$file = "test.xml";
$fp = fopen($file, "r");
while ($data=fread($fp, filesize($file)))
{
xml_parse($parser,$data,feof($fp)) or
die (sprintf("XML Error: %s at line %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}
xml_parser_free($parser);
}
using this in the start()
function can select the right node but it doesn't have any effect on the reading process:
if(($element_name == "USER") && $element_attrs["ID"] && ($element_attrs["ID"] == "$id"))
any help would be appreciated
UPDATE: XMLReader works but when using if statement it stops working:
foreach ($filteredUsers as $user) {
echo "<table border='1'>";
foreach ($user->getChildElements('post') as $index => $post) {
if( $post->getChildElements('client') == "operator" ){
printf("<tr><td class='blue'>%s</td><td class='grey'>%s</td></tr>", $post->getChildElements('message'), $post->getChildElements('time'));
}else{
printf("<tr><td class='green'>%s</td><td class='grey'>%s</td></tr>", $post->getChildElements('message'), $post->getChildElements('time'));
}
}
echo "</table>";
}
As suggested in a comment earlier, you can alternatively use the
XMLReader
Docs.It is a class (with the same name:
XMLReader
) which can open a file. By default you usenext()
to move to the next node. You would then check if the current position is at an element and then if the element has the name you're looking for and then you could process it, for example by reading the outer XML of the elementXMLReader::readOuterXml()
Docs.Compared with the callbacks in the Expat parser, this is a little burdensome. To gain more flexibility with
XMLReader
I normally create myself iterators that are able to work on theXMLReader
object and provide the steps I need.They allow to iterate over the concrete elements directly with
foreach
. Here is such an example:I have create an XML file that contains some more posts like in your question, numbered in the
id
attribute from one and up:Then I created an array with two ID values of the user interested in:
It will be used in the filter-condition later. Then the
XMLReader
is created and the XML file is opened by it:The next step creates an iterator over all
<user>
elements of that reader:Which are then filtered for the
id
attribute values stored into the array earlier:The rest is iterating with
foreach
now as all conditions have been formulated:which will return the XML of the users with the IDs 3 and 8:
The
XMLReaderNode
which is part of the XMLReader iterators does also provide aSimpleXMLElement
Docs in case you want to easily read values inside of the<user>
element.The following example shows how to get the count of
<post>
elements inside the<user>
element:This would then display
Number of posts: 1
for the user ID 3 andNumber of posts: 3
for the user ID 8.However, if that outer XML is large, you don't want to do that and you want to continue to iterate inside that element:
Which produces the following output:
This example shows: depending on how large the nested children are, you can traverse further with the iterators available via
getChildElements()
or you can use as well the common XML parser likeSimpleXML
or evenDOMDocument
on a subset of the XML.