PHP
--
Questions
--
Followers
Top Experts
I am trying to create a put rest api for an xml file of users. I have a make an edit then rebuild to send the request but I cannot extract the data from statistic_category or category_type.
<users>
<user>
<primary_id>ID</primary_id>
<first_name>Jet</first_name>
<last_name>Bleu</last_name>
<user_statistics>
<user_statistic segment_type="External">
<statistic_category desc="Faculty of Business">BUSIT</statistic_category>
<category_type desc="CAT3 DC">CAT3</category_type>
</user_statistic>
</user_statistics>
</user>
<primary_id>ID1</primary_id>
<first_name>Aire</first_name>
<last_name>Esprit</last_name>
<user_statistics>
<user_statistic segment_type="External">
<statistic_category desc="Faculty of Media">MEDIA</statistic_category>
<category_type desc="CAT3 DC">CAT3</category_type>
</user_statistic>
</user_statistics>
</user>
</users
I have tried different ways and my final attempt is with xpath but I am having no luck extracting the information neither is the code looping so I need some guidance:
<?php
$xml = new DOMDocument();
$xml->load('mypatrons.xml');
// Loop through all <user> elements
$users = $xml->getElementsByTagName('user');
foreach ($users as $user) {
$primaryId = $user->getElementsByTagName('primary_id')->item(0)->nodeValue;
$firstName = $user->getElementsByTagName('first_name')->item(0)->nodeValue;
$lastName = $user->getElementsByTagName('last_name')->item(0)->nodeValue;
$xpath = new DomXPath($xml);
$allstatCategory = $xpath->query('//user/user_statistics/user_statistic/statistic_category')[0];
$allstatCatDesc = $xpath->query('//user/user_statistics/user_statistic/statistic_category/@desc')[0];
$allcatType = $xpath->query('//user/user_statistics/user_statistic/category_type')[0];
$allcatTypeDesc = $xpath->query('//user/user_statistics/user_statistic/category_type/@desc')[0];
foreach ($allstatCategory as $statCategory) {
echo $statCategory;
}
foreach ($allstatCatDesc as $statCatDesc) {
echo $statCatDesc;
}
foreach ($allcatType as $catType) {
echo $catType;
}
foreach ($allcatTypeDesc as $allcatTypeDesc) {
echo $catTypeDesc;
}
// Create the new XML data for PUT request body
$newXml = new SimpleXMLElement('<user/>');
// Populate the XML structure for each user
$newXml->addChild('record_type', 'PUBLIC')->addAttribute('desc', 'Public');
$newXml->addChild('primary_id', $primaryId);
$newXml->addChild('first_name', $firstName);
$newXml->addChild('last_name', $lastName);
$newXml->addChild('full_name', $firstName . ' ' . $lastName);
$userStatistics = $newXml->addChild('user_statistics');
$userStatistic = $userStatistics->addChild('user_statistic');
$userStatistic->addAttribute('segment_type', 'External');
$userStatistic->addChild('statistic_category', $statCategory)->addAttribute('desc', $statCatDesc);
$userStatistic->addChild('category_type', $catType)->addAttribute('desc', $catTypeDesc);
}
print'<pre>';
print_r($newXml->asXML());
?>
Zero AI Policy
We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.
That initial xml snippet you provided looks incorrect. Is that an actual example or was it modified to be shorter for this question? I'm only asking because it doesn't look like you're checking for errors when you try to load the xml for reading.
Second question - given the plural name “user_statistics”, it seems like there is a possibility that a user could have multiple entries in there. Your code seems to only try to pull the first one, whatever it is. Should the code be looking for some kind of condition (e.g. the first statistic with an “External” segment type) ?
Finally, the xpath is going to search the entire document and return a list of elements that match the path criteria. So if you're always grabbing the first result from that xpath query, then it's going to use the result from the first <user> every single time, even if you're trying to loop through the second user.
It might be easier / clearer to just navigate the structure from node to node, similar to how you're getting the other values like first name.
Thanks for commenting. To answer your question, the xml was edited so that I could focus on user statistics. The original record is quite lengthy. Secondly, that field only has 1 entry and there are no plans to make any adjustments.
When I originally looked at doing this, I used something like this:
$statCategory = $user->getElementsByTagName('statistic_category')->item(0)->nodeValue;
$statCatDesc = $statCategory->getAttribute('desc')->nodeValue;
$catType = $user->getElementsByTagName('category_type')->item(0)->nodeValue;
$catTypeDesc = $catType->getAttribute('desc');
I had no luck with that and based on my reading it seems that this tag is different. I want to get at the nested data so this approach wouldn't work. After a few false starts, I thought of using xpath and what you see here is what I got stuck at.
You're actually REALLY close with what you used originally.
The reason it didn't work is because “nodeValue” returns the raw value, which is a string.

The purple highlighted bit of code returns a DOM element object (DOMElement). The yellow part then just grabs the inner text/value of that object.
So $statCategory becomes the string “BUSIT” for the first <user>, for example.
When you try to run this:
$statCatDesc = $statCategory->getAttribute('desc')->nodeValue;
…it's going to throw an error because you're expecting $statCategory to be a DOMElement, but it's the string value, and so PHP is like, “I don't know how to call getAttribute() on a string!”
So the trick is to separate it out. Capture the node in a separate variable then access the value and the attribute, like this:
// Get the node
$statCategoryNode = $user->getElementsByTagName('statistic_category')->item(0);
// Get the value and then the description of the node
$statCategory = $statCategoryNode->nodeValue;
$statCatDesc = $statCategoryNode->getAttribute('desc');
So the code might look like this:
<?php
$xml = new DOMDocument();
$xml->load('mypatrons.xml');
// Loop through all <user> elements
$users = $xml->getElementsByTagName('user');
foreach ($users as $user) {
$primaryId = $user->getElementsByTagName('primary_id')->item(0)->nodeValue;
$firstName = $user->getElementsByTagName('first_name')->item(0)->nodeValue;
$lastName = $user->getElementsByTagName('last_name')->item(0)->nodeValue;
// Get the node
$statCategoryNode = $user->getElementsByTagName('statistic_category')->item(0);
// Get the value and then the description of the node
$statCategory = $statCategoryNode->nodeValue;
$statCatDesc = $statCategoryNode->getAttribute('desc');
// Get the node
$catTypeNode = $user->getElementsByTagName('category_type')->item(0);
// Get the value and then the description of the node
$catType = $catTypeNode->nodeValue;
$catTypeDesc = $catTypeNode->getAttribute('desc');
// Create the new XML data for PUT request body
$newXml = new SimpleXMLElement('<user/>');
// Populate the XML structure for each user
$newXml->addChild('record_type', 'PUBLIC')->addAttribute('desc', 'Public');
$newXml->addChild('primary_id', $primaryId);
$newXml->addChild('first_name', $firstName);
$newXml->addChild('last_name', $lastName);
$newXml->addChild('full_name', $firstName . ' ' . $lastName);
$userStatistics = $newXml->addChild('user_statistics');
$userStatistic = $userStatistics->addChild('user_statistic');
$userStatistic->addAttribute('segment_type', 'External');
$userStatistic->addChild('statistic_category', $statCategory)->addAttribute('desc', $statCatDesc);
$userStatistic->addChild('category_type', $catType)->addAttribute('desc', $catTypeDesc);
}
print'<pre>';
print_r($newXml->asXML());
?>






EARN REWARDS FOR ASKING, ANSWERING, AND MORE.
Earn free swag for participating on the platform.
Hey Marcia,
Have you considered using the SimpleXML parser instead of DOMDocument. Might be easier to navigate:
$xml = simplexml_load_file("mypatrons.xml") or die("Error: Cannot Create XML Object");
foreach($xml->children() as $user):
$userStat = $user->user_statistics[0]->user_statistic;
echo $userStat->statistic_category; // Node Value
echo $userStat->statistic_category['desc']; // Attribute Value
endforeach;
@gr8gonzo, I am getting an error. It doesn't seem to be capturing the node value:
PHP Warning: Attempt to read property "nodeValue" on null in /srv/web/sites/webapps.library.uoit.ca/htdocs/ojs/test.php on line 18
PHP Fatal error: Uncaught Error: Call to a member function getAttribute() on null in /srv/web/sites/webapps.library.uoit.ca/htdocs/ojs/test.php:19
@Chris Stanyon, appreciate your input but I would like to continue with the DOM solution just to finish what I've started.
Fair enough.
Once you've got that bit sorted, you'll still want to work on your foreach loop as you currently loop over the users and overwrite the values each time, so you're only ever going to get the last entry.

Get a FREE t-shirt when you ask your first question.
We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.
@gr8gonzo, thanks that did the trick. I also want to include records without a statistical category but I have figured that one out.
@Chris Stanhope, thanks for the suggestion. Got that figured out as well.
PHP
--
Questions
--
Followers
Top Experts
PHP is a widely-used server-side scripting language especially suited for web development, powering tens of millions of sites from Facebook to personal WordPress blogs. PHP is often paired with the MySQL relational database, but includes support for most other mainstream databases. By utilizing different Server APIs, PHP can work on many different web servers as a server-side scripting language.