Hallo allemaal,

Ik loop tegen een probleem aan met het uitlezen van een xml bestand met de CBC namespace formattering.

Het XML bestand wordt als volgt ingeladen:
<?php
$xml_file = 'INV-120024175_CUS-1000637-multiple_orders.XML';
$xmlfile = file_get_contents( $xml_file );
$ob = simplexml_load_string($xmlfile, 'SimpleXMLElement', LIBXML_NOCDATA);

Vervolgens loop ik door de xml met:
/Init the array     
$string_test = []; 
foreach($ob->xpath('//cac:InvoiceLine') as $invoice) {
         
$string_ID = (string)$invoice->children('cbc', true)->ID.PHP_EOL;         
$string_test [$string_ID]['ID'] = (string)$invoice->children('cbc', true)->ID.PHP_EOL;         
$string_test [$string_ID]['Note'] = (string)$invoice->children('cbc', true)->Note.PHP_EOL;         
$string_test [$string_ID]['InvoicedQuantity'] = (string)$invoice->children('cbc', true)->InvoicedQuantity.PHP_EOL;     
    
$string_test [$string_ID]['LineExtensionAmount'] = (string)$invoice->children('cbc', true)->LineExtensionAmount.PHP_EOL; //Non working code to get the contents of the item node...         

$item = $invoice->xpath('//cac:Item');          
$array = json_decode(json_encode($item), TRUE); 
#print_r($array); 

//echo "<br>" . (string)$item->children('cbc', true)->Name.PHP_EOL; 

} 

//Print the full array foreach($string_test as $string){            
print_r($string); 
} 
?>


Voor de top level items werkt het perfect, maar zodra ik bijvoorbeeld binnenin cbc:item waardes wil uitlezen loop ik vast. Iemand die er ervaring mee heeft? Via deze link is een voorbeeld te zien van de XML file: https://stackoverflow.com/questions/39314888/how-to-parse-the-below-xml-file-in-php

Misschien heb je hier iets aan:
<?php
foreach ($invoice->xpath('//cac:Item') as $item) {
    $cbc = $item->xpath('descendant::cbc:*'); // array van SimpleXMLElement objecten van alle onderliggende elementen met een cbc namespace
    foreach ($cbc as $elt) {
        echo $elt->getName().': '.$elt.'<br>'; // voor $elt wordt de __toString() methode aangeroepen
    }
}
?>

Dit levert (met het XML document uit bovenstaande StackOverflow thread):
Description: TITLE
ID: 9789079995318
Name: Afnemer
Value: ABC


Op een soortgelijke wijze zou je de selectie van de Invoice elementen (ID, InvoicedQuantity) ook kunnen selecteren met de "XML axis" child::.
Hallo Thomas,

Bedankt! Ik ga het morgen gelijk testen. Ik dacht al dat het iets met de notatie te maken, maar dat er "descendents::" gebruikt moest worden was ik zelf zo niet opgekomen.
Ik ook niet, heb hier best lang op zitten zoeken, maar xpath is een ontzettend krachtige "querytaal" voor XML-structuren (waar ik verder nauwelijks iets van weet :p). Het is "gewoon" een kwestie van de juiste vraag (aan XML) formuleren, en dan uitzoeken hoe je dat opschrijft in het xpath-dialect.
Hey Thomas,

Het is me bijna gelukt! Ik zit nog met één kleine issue en dan heb ik het opgelost. Ik heb me suf gezocht op de juiste selector(s). Ik snap nu je woorden dat het lastig te vinden is :P

In mijn originele foreach, waarin ik door alle invoicelines loop, heb ik een nieuwe foreach gemaakt met het doel om van de invoiceline in kwestie de "item" eruit te halen en deze waardes toe te voegen aan de originele array. Hiervoor heb ik je code gebruikt met 'descendant::cbc:*' om deze op te halen. Volgens heb ik deze in een nieuwe array gestopt met de naam van de node als de Key en de waarde als value. Deze array voeg ik dan weer toe aan de originele onder de key "item". Alleen komt er dan overal alleen 't laatste item uit de laatste invoiceline bij te staan.

Ik zie even niet wat ik fout doe, heb jij een idee?
<?php
  //Init the array
    $invoiceLineArray = [];
    $itemArray = [];

    foreach($ob->xpath('//cac:InvoiceLine') as $invoice)
    {
        $invoiceLineID = (string)$invoice->children('cbc', true)->ID.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['ID'] = (string)$invoice->children('cbc', true)->ID.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['Note'] = (string)$invoice->children('cbc', true)->Note.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['InvoicedQuantity'] = (string)$invoice->children('cbc', true)->InvoicedQuantity.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['LineExtensionAmount'] = (string)$invoice->children('cbc', true)->LineExtensionAmount.PHP_EOL;

        foreach ($invoice->xpath('//cac:Item') as $item) {
            $children = $item->xpath('descendant::cbc:*'); // array van SimpleXMLElement objecten van alle onderliggende elementen met een cbc namespace
            
            //print_r($children); // -> deze print alle items uit elke invoiceline uit

            foreach ($children as $child) {

               // print_r($child);

                $name = $child->getName();

                $itemArray[$name] = $child.PHP_EOL;

              // print_r($itemArray);

             }
            $invoiceLineArray [$invoiceLineID]['Item'] = $itemArray; // -> Maak een nieuwe key aan en vul deze met de items uit "Item"
        }
       
    }

    //print_r( $itemArray, false );

    print_r($invoiceLineArray, false); // -> print alleen het item uit de laatste invoiceline
?>


[size=xsmall]Toevoeging op 16/07/2020 11:10:48:[/size]

Een update, ben inmiddels wel its dichterbij gekomen.

Via deze test:

<?php
    $abc = 3;
    $invoicelinetest = $ob->xpath('//cac:InvoiceLine['.$abc.']/cbc:ID');
    echo $invoicelinetest[0];
?>


Ben ik er achter gekomen dat ik op deze manier de ID van invoiceline nummer 3 kan pakken.

Nu bracht me dit op het volgende idee, een $count variabelen toevoegen aan de foreach, zodat ik weet om welke invoiceline het gaat. Vervolgens wil ik deze uitlezen met:

<?php

 foreach($ob->xpath('//cac:InvoiceLine') as $invoice)
    {
        $count += 1;
        $invoiceLineID = (string)$invoice->children('cbc', true)->ID.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['ID'] = (string)$invoice->children('cbc', true)->ID.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['Note'] = (string)$invoice->children('cbc', true)->Note.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['InvoicedQuantity'] = (string)$invoice->children('cbc', true)->InvoicedQuantity.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['LineExtensionAmount'] = (string)$invoice->children('cbc', true)->LineExtensionAmount.PHP_EOL;
       
        foreach ($invoice->xpath('//['.$count.']/cac:Item') as $item) {

?>


Ik zit nog even te stoeien met hoe ik het nummer van de invoiceLine (de count variabelen) in een goede xpath variabelen kan krijgen..
Heb je gecontroleerd of ID niet toevallig hetzelfde is voor alle InvoiceLines? Dat zou verklaren waarom je array maar één entry heeft (dezelfde index wordt telkens overschreven).

De betekenis is mij niet direct duidelijk, maar als je een unieke index nodig hebt is OrderLineReference.LineID misschien een betere kandidaat, of zijn dit nummers uit verschillende systemen?
Wat late reactie, maar ik heb het wel werkend gekregen gelukkig :D


<?php
//Initialize the arrays
    $invoiceLineArray = [];
    $itemArray = [];
    $sellersItemIDArray = [];
    $priceArray = [];

    $count = 0;

    foreach($ob->xpath('//cac:InvoiceLine') as $invoice)
    {
        $count += 1;
        $invoiceLineID = (string)$invoice->children('cbc', true)->ID.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['ID']                    = (string)$invoice->children('cbc', true)->ID.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['Note']                  = (string)$invoice->children('cbc', true)->Note.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['InvoicedQuantity']      = (string)$invoice->children('cbc', true)->InvoicedQuantity.PHP_EOL;
        $invoiceLineArray [$invoiceLineID]['LineExtensionAmount']   = (string)$invoice->children('cbc', true)->LineExtensionAmount.PHP_EOL;
        
        //Fill the array with the descendants of the cac:Item node
        foreach ($invoice->xpath('//cac:InvoiceLine['. $count .']/cac:Item') as $item) {
            $children = $item->xpath('descendant::cbc:*'); // array van SimpleXMLElement objecten van alle onderliggende elementen met een cbc namespace
            
            foreach ($children as $child) {
                $name = $child->getName();
                $itemArray[$name]  = $child.PHP_EOL;
             }
             
        }

        //Fill the array with the descendants of the cac:SellersItemIdentification node
        foreach ($invoice->xpath('//cac:InvoiceLine['. $count .']/cac:Item/cac:SellersItemIdentification') as $item) {
            $children = $item->xpath('descendant::cbc:*'); // array van SimpleXMLElement objecten van alle onderliggende elementen met een cbc namespace
            foreach ($children as $child) {
                $name               = $child->getName();
                $sellersItemIDArray[$name]   = $child.PHP_EOL;
             }
        }

        //Fill the array with the descendants of the cac:Price node
        foreach ($invoice->xpath('//cac:InvoiceLine['. $count .']/cac:Price') as $item) {
            $children = $item->xpath('descendant::cbc:*'); // array van SimpleXMLElement objecten van alle onderliggende elementen met een cbc namespace
            foreach ($children as $child) {
                $name               = $child->getName();
                $priceArray[$name]  = $child.PHP_EOL;
             }
        }

        $invoiceLineArray [$invoiceLineID]['Item']  = $itemArray; //Fill the items
        $invoiceLineArray [$invoiceLineID]['SellersItemIdentification']  = $sellersItemIDArray; //Fill the items
        $invoiceLineArray [$invoiceLineID]['Price'] = $priceArray; //Fill the items

    }

?>

Reageren