Search code examples
phpxmlsimplexmlxmlreader

Using XMLReader and SimpleXML to retrieve data from an atrribute with a specific parameter name


I am trying to learn how to use XMLReader with SimpleXML to read a large xml file and to be able to retrieve various data from it. My problem is retrieving data from elements by their attribute.

For example if i have the XML:

<Product>
    <CustomParameter Name="companyName">Company A</CustomParameter>
    <CustomParameter Name="productName">Shiny Black Shoes</CustomParameter>
    <CustomParameter Name="productUrl">http://www.example.com</CustomParameter>
    <CustomParameter Name="companyUrl">http://www.example.com</CustomParameter>
</Product>
<Product>
    <CustomParameter Name="companyName">Company B</CustomParameter>
    <CustomParameter Name="productName">Boots</CustomParameter>
    <CustomParameter Name="productUrl">http://www.example.com</CustomParameter>
    <CustomParameter Name="companyUrl">http://www.example.com</CustomParameter>
</Product>

I want to only retrieve the data for the CustomParameter with the name="productName" attribute.

I am using this code, but its only displaying the first found CustomParameter.

$z = new XMLReader;
$z->open('products.xml');

$doc = new DOMDocument;

$product_name = array();

// move to the first <product /> node
while ($z->read() && $z->name !== 'Product');

while ($z->name === 'Product')
{
    $node = simplexml_import_dom($doc->importNode($z->expand(), true));

    $product_name[] = $node->CustomParameter;

    $z->next('Product');
 }

 $product_name = array_unique($product_name);

foreach($product_name as $value)
     echo $value."\n";

Can someone explain how to read the specific one I want?

Thanks


Solution

  • In the product while loop, you could iterate over each of the CustomParameter tags, testing for the attribute value, like this:

    while ($z->name === 'Product')
    {
        $node = simplexml_import_dom($doc->importNode($z->expand(), true));
    
        foreach($node->children() as $child) {
            if ($child["Name"] == "productName") {
                $product_name[] = (string) $child;
            }
        }
    
        $z->next('Product');
    }
    

    However, you could make your code a lot shorter if you would use an xpath search, like this:

    $xmlDoc = simplexml_load_file('products.xml');
    
    // locate the nodes of interest through an XPath descriptor:
    $result = $xmlDoc->xpath('/Products/Product/CustomParameter[@Name="productName"]');
    
    while(list( , $node) = each($result)) {
        $product_name[] = (string) $node;
    }
    

    In the above code you should replace the XPath value with the true path to your elements. Since you did not provide the whole XML document, I just assumed the Product tags appear in a Products (plural) wrapper tag, which is the root element. Of course, your actual situation can be different, but it should be easy to adapt to.