سؤال

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?

هل كانت مفيدة؟

المحلول

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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top