0

I have following XML:

<?xml version="1.0" encoding="Windows-1250"?>
<rsp:responsePack version="2.0" id="Za001" state="ok" note="" programVersion="10600.125 E1 (22.1.2014)"
                  xmlns:rsp="http://www.stormware.cz/schema/version_2/response.xsd"
                  xmlns:lst="http://www.stormware.cz/schema/version_2/list.xsd"
                  xmlns:lStk="http://www.stormware.cz/schema/version_2/list_stock.xsd"
                  xmlns:stk="http://www.stormware.cz/schema/version_2/stock.xsd">
    <rsp:responsePackItem version="2.0" id="a55" state="ok">
        <lStk:listStock version="2.0" dateTimeStamp="2014-04-18T22:36:44" dateValidFrom="2014-04-18" state="ok">
            <lStk:stock version="2.0">
                <stk:stockHeader>
                    <stk:id>101</stk:id>
                    <stk:EAN>8594011770127</stk:EAN>
                    <stk:isSales>true</stk:isSales>
                    <stk:name>Item1</stk:name>
                    <stk:categories>
                        <stk:idCategory>5</stk:idCategory>
                        <stk:idCategory>6</stk:idCategory>
                    </stk:categories>
                </stk:stockHeader>
            </lStk:stock>
            <lStk:stock version="2.0">
                <stk:stockHeader>
                    <stk:id>114</stk:id>
                    <stk:EAN>8595557507840</stk:EAN>
                    <stk:name>Item2</stk:name>
                    <stk:categories>
                        <stk:idCategory>6</stk:idCategory>
                        <stk:idCategory>9</stk:idCategory>
                        <stk:idCategory>1</stk:idCategory>
                    </stk:categories>
                </stk:stockHeader>
            </lStk:stock>
        </lStk:listStock>
    </rsp:responsePackItem>
</rsp:responsePack>

There are two items with the lStk:stock attribute, and foreach of them I would need to get some values.

My current code is:

$xml=simplexml_load_file("import.xml");
if ($xml) {
    $ns = $xml->getDocNamespaces();
    $data = $xml->children($ns['rsp']);

    $items = $data->children($ns['lStk']);
    foreach ($items as $item) {
        $counter = 0;
        $elements = $item->children($ns['lStk'])->children($ns['stk'])->children($ns['stk']);
        $products[$counter]['ean'] = $elements->EAN;
        $products[$counter]['name'] = $elements->name;
        $products[$counter]['count'] = $elements->count;

        $i = 0;
        foreach ($elements->categories as $category) {
            /** @var $ class_name */
            foreach ($category as $cat) {
                /** @var $cat class_name */
                $products[$counter]['category'][$i] = $cat;
                $i++;
            }
        }
    }
}
var_dump($products);

Which returns the info only for the first product. How should I change it to get both products?

1 Answer 1

1

The problem that you get only one item is that you fetch only one item.

This might sound a bit moot, point in case is, that you're putting the parent element into $item which actually is there only once.

So you're not stepping deep enough. Step one more deep to iterate over the correct children and it works like you planned it:

$xml  = simplexml_load_file($path_to_xml_file);
$ns   = $xml->getDocNamespaces();
$data = $xml->children($ns['rsp']);

$list  = $data->children($ns['lStk']); // first the list
$items = $list->children($ns['lStk']); // then the items in that list

$products = [];

foreach ($items as $item)
{
    $elements = $item->children($ns['stk'])->children($ns['stk']);
                // ^^^ one less here compared to yours

    $product = array_intersect_key((array)$elements, ['EAN' => 0, 'name' => 1]);

    foreach ($elements->categories as $category) foreach ($category as $cat)
        $product['category'][] = (string)$cat
    ;

    $products[] = $product;
}

var_dump($products);

Output:

array(2) {
  [0] => array(3) {
    'EAN'      =>  string(13) "8594011770127"
    'name'     => string(5) "Item1"
    'category' => array(2) {
      [0] => string(1) "5"
      [1] => string(1) "6"
    }
  }
  [1] => array(3) {
    'EAN'      => string(13) "8595557507840"
    'name'     => string(5) "Item2"
    'category' => array(3) {
      [0] => string(1) "6"
      [1] => string(1) "9"
      [2] => string(1) "1"
    }
  }
}

The code is a little different to yours but that is merely cosmetic. However it gives you some ideas how to not $count and $i++ too much by just using an array.

For more ease of use with XML-Namespace and simplexml especially with traversal, normally xpath helps here dramatically:

$xml = simplexml_load_file($path_to_xml_file);

$products = [];

$items = $xml->xpath('//lStk:listStock//stk:stockHeader');

foreach ($items as $item)
{
    $product             = array_intersect_key((array)$item->children('stk', TRUE), ['EAN' => 0, 'name' => 1]);
    $product['category'] = array_map('intval', $item->xpath('*[local-name()="categories"]/*'));

    $products[] = $product;
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, your code as is doesn't work, as it throws errors on the $products = [] (is this Javascript syntax for array initialization?), but it gave me the idea what I'm doing wrong, so I marked the answer as accepted. Would be cool if you could fix the code, as I'm unable to...
[] is short notation for array() in PHP's syntax since PHP 5.4. If your server does not support it, contact your hoster because every version < PHP 5.4 is end-of-life: php.net/releases/5_3_20.php (No critical security fixes even any longer since Dec 2013 even)
OK, thanks, didn't know about that! I'll switch my PHP to higher version.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.