从zoho XML文件中创建key => value数组

I have this XML file, and basically I want to be able to construct an array []key=>value such as

[0]['Product Id']=>productId_1,
[0]['Product Name']=>product_name_1...
[1]['Product Id']=>productId_2,
[1]['Product Name']=>product_name_2

and so on. I've tried simplexml_load_file, and struggled to reach the node (getName(), attributes()) with naught success. A print_r($obj) shows a numbered array under FL, which I can reach, but I want the names ("Product ID") as well as the values. That I cannot do. I'm not at all familiar with XML, could anyone give a little help please? Couldn't find anything to match my problem in a 2 days search. Hope I explained myself properly.

<Invoices>
    <row num="1">
        <FL val="Product Details">
            <product no="1">
                <FL val="Product Id">productId_1</FL>
                <FL val="Product Name">product_name_1</FL>
                <FL val="Quantity">1</FL>
                <FL val="List Price">1.00</FL>
                <FL val="Discount">0</FL>
                <FL val="Total">1.00</FL>
                <FL val="Total After Discount">1.00</FL>
                <FL val="Tax">0</FL>
                <FL val="Net Total">1.00</FL>
            </product>
        </FL>
    </row>
    <row num="....">
    </row>
</Invoices>

Let's take a look how this works in SimpleXML:

<FL val="Product Id">productId_1</FL>
       ^^^              ^^^
        |                |
 "val" attribute         |
                   <FL> element

Let's say this XML <FL>-element would be stored in variable $FL, you then can access the ...

  • ... "val" attribute value with $FL['val']
  • ... <FL> element value with $FL directly.

To put that into an array, it is important to convert the values to strings. I use the trim function for that, casting to string with (string) would work, too:

$array = array();
$key   = trim($FL['val']);
$value = trim($FL);
$array[$key] = $value;

Exemplary $array then is:

Array
(
    [Product Id] => productId_1
)

So now in the XML there are multiple <FL>-elements within the <product>-elements. Let's say that $product would be the first product-element. The lines from above only need to be wrapped inside a foreach loop over all <FL>-elements:

$array = array();
foreach ($product->FL as $FL) {
    $key   = trim($FL['val']);
    $value = trim($FL);
    $array[$key] = $value;
}

Exemplary $array then is:

Array
(
    [Product Id] => productId_1
    [Product Name] => product_name_1
    [Quantity] => 1
    [List Price] => 1.00
    [Discount] => 0
    [Total] => 1.00
    [Total After Discount] => 1.00
    [Tax] => 0
    [Net Total] => 1.00
)

Now finally you only need to convert all products. Because it works for all products as it works for one product, you can put the logic into a function of it's own:

function product(SimpleXMLElement $product)
{
    $array = array();
    foreach ($product->FL as $FL) {
        $key         = trim($FL['val']);
        $value       = trim($FL);
        $array[$key] = $value;
    }
    return $array;
}

Then you use an xpath query to obtain all product elements and do the mapping:

$xml = simplexml_load_string($buffer);

$result = array_map('product', $xml->xpath('//product'));

The output then is:

Array
(
    [0] => Array
        (
            [Product Id] => productId_1
            [Product Name] => product_name_1
            [Quantity] => 1
            [List Price] => 1.00
            [Discount] => 0
            [Total] => 1.00
            [Total After Discount] => 1.00
            [Tax] => 0
            [Net Total] => 1.00
        )

)

I hope this makes things a little more clear for you.

And here the example in full:

<?php
/**
 * Make key=>value array from zoho XML file
 * @link http://stackoverflow.com/a/29461013/367456
 */

$buffer = <<<XML
<Invoices>
    <row num="1">
        <FL val="Product Details">
            <product no="1">
                <FL val="Product Id">productId_1</FL>
                <FL val="Product Name">product_name_1</FL>
                <FL val="Quantity">1</FL>
                <FL val="List Price">1.00</FL>
                <FL val="Discount">0</FL>
                <FL val="Total">1.00</FL>
                <FL val="Total After Discount">1.00</FL>
                <FL val="Tax">0</FL>
                <FL val="Net Total">1.00</FL>
            </product>
        </FL>
    </row>
    <row num="....">
    </row>
</Invoices>
XML;

function product(SimpleXMLElement $product)
{
    $array = array();
    foreach ($product->FL as $FL) {
        $key         = trim($FL['val']);
        $value       = trim($FL);
        $array[$key] = $value;
    }
    return $array;
}

$xml = simplexml_load_string($buffer);

$result = array_map('product', $xml->xpath('//product'));

print_r($result);

Use a DOMDocument combined with some DOMXPath queries:

$source = <<<EOS
       <Invoices>
       <row num="1">
            <FL val="Product Details">
                <product no="1">
                    <FL val="Product Id">productId_1</FL>
                    <FL val="Product Name">product_name_1</FL>
                    <FL val="Quantity">1</FL>
                    <FL val="List Price">1.00</FL>
                    <FL val="Discount">0</FL>
                    <FL val="Total">1.00</FL>
                    <FL val="Total After Discount">1.00</FL>
                    <FL val="Tax">0</FL>
                    <FL val="Net Total">1.00</FL>
                </product>
           </FL>
        </row>
        <row num="2">
            <FL val="Product Details">
                <product no="2">
                    <FL val="Product Id">productId_2</FL>
                    <FL val="Product Name">product_name_2</FL>
                    <FL val="Quantity">2</FL>
                    <FL val="List Price">2.00</FL>
                    <FL val="Discount">0</FL>
                    <FL val="Total">2.00</FL>
                    <FL val="Total After Discount">2.00</FL>
                    <FL val="Tax">0</FL>
                    <FL val="Net Total">2.00</FL>
                </product>
           </FL>
        </row>
    </Invoices>
EOS;

$dom = new DOMDocument();
$dom->loadXML($source);

$xpath = new DOMXPath($dom);
$products = $xpath->query('//product');

foreach ($products as $product) {
    $fls = $xpath->query('.//FL', $product);
    $row = [];
    foreach ($fls as $fl) {
        $row[$fl->getAttribute('val')] = $fl->nodeValue;
    }
    $result[] = $row;
}

print_r($result);

Output:

Array
(
    [0] => Array
        (
            [Product Id] => productId_1
            [Product Name] => product_name_1
            [Quantity] => 1
            [List Price] => 1.00
            [Discount] => 0
            [Total] => 1.00
            [Total After Discount] => 1.00
            [Tax] => 0
            [Net Total] => 1.00
        )

    [1] => Array
        (
            [Product Id] => productId_2
            [Product Name] => product_name_2
            [Quantity] => 2
            [List Price] => 2.00
            [Discount] => 0
            [Total] => 2.00
            [Total After Discount] => 2.00
            [Tax] => 0
            [Net Total] => 2.00
        )
)