PHP Count Xml Elements

php count xml elements

With DOM you can either use

$dom->getElementsByTagName('OfferName')->length;

to count all OfferName elements only. length is an attribute of DOMNodeList.

To count all OfferName elements within an OfferNameList, you can use DOMXPath::evaluate

$xpath->evaluate('count(//OfferNameList/OfferName');

Note that within is somewhat inaccurate here as the XPath query will only consider direct children. Please adjust your question if you need OfferName elements anywhere below a OfferNameList element.

Also note that // will query anywhere in the document, which might be less efficient for large documents. If you know OfferNameList elements occur at a certain position in your XML only, use a direct path.


Full working example (run on codepad):

$xml = <<< XML
<root>
<NotOfferNameList>
<OfferName>...</OfferName>
<OfferName>...</OfferName>
<OfferName>...</OfferName>
</NotOfferNameList>
<OfferNameList>
<OfferName>...</OfferName>
<OfferName>...</OfferName>
<OfferName>...</OfferName>
</OfferNameList>;
</root>
XML;

$dom = new DOMDocument;
$dom->loadXml($xml);

// count all OfferName elements
echo $dom->getElementsByTagName('OfferName')->length, PHP_EOL; // 6

// count all OfferNameList/OfferName elements
$xp = new DOMXPath($dom);
echo $xp->evaluate('count(//OfferNameList/OfferName)'); // 3

PHP - Count XML Child Nodes

you can simply use this:

echo $xml->response->services->service->count();

or you use a loop:

$xml = new SimpleXMLElement($result);

foreach ($xml as $services) {
foreach($services as $service) {
echo "<pre>";
print_r($service->count());
echo "</pre>";
}
}

How to count nodes of an xml file with php?

You can't use the simple XML parser as a DOM document. Quite simply getElementsByTagName doesn't exist in SimpleXML. Use this instead:

$pars_emsc = new DOMDocument( "1.0", "ISO-8859-15" );
$pars_emsc->load("/file");
$count_folder_emsc= $pars_emsc->getElementsByTagName("Document")[0]->getElementsByTagName('Folder')->length;

print_r($count_folder_emsc);

Alternatively just do:

$pars_emsc= simplexml_load_file("/file");
$count_folder_emsc= $pars_emsc-> Document -> Folder -> count();

count xml elements/nodes using SimpleXML

This should work for you:

echo count($xml->channel->item);

EDIT:

Why you have to it this way? Because form simplexml_load_file() you get a structure like this (print_r($xml);):

SimpleXMLElement Object
(
[@attributes] => Array
(
[version] => 2.0
)

[channel] => SimpleXMLElement Object
(
[item] => Array
(

Because of this structure you have to access the object like i said (above).

Count all elements of a certain name in an XML file using PHP

It is easily done with XPath:

$doc = new DOMDocument();
$doc->load('my_file.xml', LIBXML_PARSEHUGE);

$xp = new DOMXPath($doc);
$count = $xp->evaluate('count(//Item)');

The XPath expression returns the number of all Item tags in the document.

The LIBXML_PARSEHUGE option only affects internal limits on the depth, entity recursion, and the size of text nodes. However, the DOM parser loads the entire document into memory.

For really huge files, use a SAX parser, which operates on each piece of XML sequentially (and thus loads only a small portion of the document into memory):

$counter = 0;

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, function ($parser, $name) use (&$counter) {
if ($name === 'ITEM') {
$counter++;
}
}, null);

if (!($fp = fopen('my_file.xml', 'r'))) {
die('Could not open XML input');
}

while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);

Get Count of XML nodes in PHP

You can get a count of SimpleXML child-nodes just by using PHP's native count function:

echo count($xml->datafile);
// 2

In this case, rather than using a for loop and a count, it might make more sense just to use a foreach loop instead:

foreach ($xml->datafile as $xmlItem) {
$fileName = "xml/";
$fileName .= $xmlItem;
...
}

See https://eval.in/951668 for a quick example

Count how many children are in XML with PHP

Try replacing foreach ($xml->alert->info[$i] as $entry) with:

 foreach ($xml->alert->info[$i] as $j => $entry)

The current item index will be $j

How to get count of distinct XML nodes?

While your solution works, and pretty efficiently given that it operates in O(n*k) time (where n is the number of nodes in the tree and k is the number of vertices), I figured I'd propose an alternative solution that doesn't rely on arrays or references and is more generalized to work, not just for XML, but for any DOM tree. This solution also operates in O(n*k) time, so it's just as efficient. The only difference is you can consume the values from a generator without having to build out the entire array first.

Modeling The Problem

The easiest way for me to understand this problem is to model it as a graph. If we model the document that way what we get are levels and vertices.

DOM tree figure1

So effectively, this allows us to divide and conquer, breaking down the problem into two distinct steps.

  1. Count the cardinal child node names of a given vertical as the sum (verticies)
  2. Find the max of the collective sum on the horizontal (levels)

Which means that if we do level-order traversal on this tree we should be able to easily produce the cardinality of node names as the maximum sum of all the verticals.

DOM tree figure2

In other words, there's a cardinality problem of getting the distinct child node names of each node. Then there's the issue of finding the maximum sum for that entire level.

Minimal, Complete, Verifiable, Self-Contained Example

So to provide a Minimal, Complete, Verifiable, and Self-Contained Example I'm going to rely on extending PHP's DOMDocument rather than the third party XML library you're using in your example.

It's probably worth noting that this code is not backwards compatible with PHP 5, (because of the use of yield from), so you must use PHP 7 for this implementation to work.

First, I'm going to implement a function in DOMDocument that allows us to iterate over the DOM tree in level-order by using a generator.

class SpecialDOM extends DOMDocument {
public function level(DOMNode $node = null, $level = 0, $ignore = ["#text"]) {
if (!$node) {
$node = $this;
}
$stack = [];
if ($node->hasChildNodes()) {
foreach($node->childNodes as $child) {
if (!in_array($child->nodeName, $ignore, true)) {
$stack[] = $child;
}
}
}
if ($stack) {
yield $level => $stack;
foreach($stack as $node) {
yield from $this->level($node, $level + 1, $ignore);
}
}
}
}

The mechanics of the function itself is actually quite simple. It doesn't rely on passing around arrays or using references, but instead uses the DOMDocument object itself, to build a stack of all child nodes in a given node. Then it can yield this entire stack at once. This is the level part. At which point we rely on recursion to yield from each element in this stack any other nodes on the next level.

Here's a very simple XML document to demonstrate how straight-forward this is.

$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>

<Data>
<Record>
<SAMPLE>Some Sample</SAMPLE>
</Record>
<Note>
<SAMPLE>Some Sample</SAMPLE>
</Note>
<Record>
<SAMPLE>Sample 1</SAMPLE>
<SAMPLE>Sample 2</SAMPLE>
</Record>
</Data>
XML;

$dom = new SpecialDOM;
$dom->loadXML($xml);

foreach($dom->level() as $level => $stack) {
echo "- Level $level\n";
foreach($stack as $item => $node) {
echo "$item => $node->nodeName\n";
}
}

The output will look like this.


- Level 0
0 => Data
- Level 1
0 => Record
1 => Note
2 => Record
- Level 2
0 => SAMPLE
- Level 2
0 => SAMPLE
- Level 2
0 => SAMPLE
1 => SAMPLE

So at least now we have a way of knowing what level a node is on and in what order it appears on that level, which is useful for what we intend to do.

Now the idea of building a nested array is actually unnecessary to obtain the cardinality sought by max_count. Because we already have access to the nodes themselves from the DOM tree. Which means we know what elements are contained therein inside of our loop at each iteration. We don't have to generate the entire array at once to begin exploring it. We can do this at a level-order instead, which is actually really cool, because it means you can build a flat array to get to max_count for each record.

Let me demonstrate how that would work.

$max = [];
foreach($dom->level() as $level => $stack) {
$sum = [];
foreach($stack as $item => $node) {
$name = $node->nodeName;
// the sum
if (!isset($sum[$name])) {
$sum[$name] = 1;
} else {
$sum[$name]++;
}
// the maximum
if (!isset($max[$level][$name])) {
$max[$level][$name] = 1;
} else {
$max[$level][$name] = max($sum[$name], $max[$level][$name]);
}
}
}

var_dump($max);

The output we get would look like this.


array(3) {
[0]=>
array(1) {
["Data"]=>
int(1)
}
[1]=>
array(2) {
["Record"]=>
int(2)
["Note"]=>
int(1)
}
[2]=>
array(1) {
["SAMPLE"]=>
int(2)
}
}

Which proves that we can calculate max_count without the need for references or complex nested arrays. It's also easier to wrap your head around when you obviate the one-way mapping semantics of PHP arrays.

Synopsis

Here's the resulting output from this code on your sample XML document.


array(5) {
[0]=>
array(1) {
["Data"]=>
int(1)
}
[1]=>
array(1) {
["Record"]=>
int(2)
}
[2]=>
array(1) {
["SAMPLE"]=>
int(2)
}
[3]=>
array(4) {
["TITLE"]=>
int(1)
["SUBTITLE"]=>
int(1)
["AUTH"]=>
int(2)
["ABSTRACT"]=>
int(1)
}
[4]=>
array(2) {
["FNAME"]=>
int(1)
["DISPLAY"]=>
int(1)
}
}

Which is identical to the max_count of each of your sub arrays.

  • Level 0
    • Data => max_count 1
  • Level 1
    • Record => max_count 2
  • Level 2
    • SAMPLE => max_count 2
  • Level 3
    • TITLE => max_count 1
    • SUBTITLE => max_count 1
    • AUTH => max_count 2
    • ABSTRACT => max_count 1
  • LEVEL 4
    • FNAME => max_count 1
    • DISPLAY => max_count 1

To get the elements for any of these nodes throughout the loop just look at $node->childNodes as you already have the tree (thus eliminating the need for references).

The only reason you needed to nest the elements into your array is because the keys of a PHP array have to be unique and since you're using the node name as the key this requires nesting to get the lower levels of the tree and still structure the value of max_count properly. So it's a data structure problem there and I solve it differently by avoiding modeling the solution after the data structure.

php count xml elements isn't work fine when there is one element

Finally I made a solution: I must check whether product is multidimensional array or not.

In first case:

Array
(
[0] => Array
([id]=>1...)
[1] => Array
([id]=>2...)
)

In Second case:

Array
(
[id]=>1...
)

Now I wrote a function to check whether it's multidimensional array or not. If the array is not multidimensional, so the count is one!

public function isMultiArray($arr){
foreach ($arr as $key) {
if (is_array($key)) {
return true;
}
else return false;
}
}


Related Topics



Leave a reply



Submit