0

I have this xml excerpt:

<Names>
<Name lang="en">Soccer</Name>
<Name lang="it">Calcio</Name>
</Names>

I would like to get an array like this

$names = array(    
   "en" => "Soccer",
   "it" => "Calcio"
)

I tried with simple_load_file() and then traversing the nodes:

for($i=0;$i<count($xmlObject->Names->Name);$i++):
  $output[$xmlObject->Names->Name[$i]['lang'] = $xmlObject->Names->Name[$i];
endfor;

but doesnot work.

2
  • SimpleXMLElement instances need to be cast to strings Commented Oct 7, 2014 at 10:56
  • You were pretty close but just made the little mistake (a common one btw) to think that you would need to specify the root element Names, but you don't need to: eval.in/204444 - but doesnot work is also not a good programming question. Commented Oct 11, 2014 at 0:36

2 Answers 2

1

The quickest way to get an array like the one you want would be to do this:

$dom = simplexml_load_file($yourFile);
$result = array();
foreach ($dom as $node)
{//iterate over elements
    if ($node->getName() === 'Name')
    {//make sure to only process Name tags
         $lang = null;
         foreach ($node->attributes() as $name => $val)
         {//find the attribtues
             if ($name == 'lang')
             {//we have the lang attribute
                 $lang = (string) $val;//cast to string!
                 break;//we're done here
             }
         }
         if ($lang)
             $result[$lang] = (string) $node;//cast node to string to get its contents
    }
}
var_dump($result);

The output will be:

array(2) {
  ["en"]=>
  string(6) "Soccer"
  ["it"]=>
  string(6) "Calcio"
}

demo here

As OIS suggested in his answer, you could also use SimpleXMLElement::xpath(), which allows you to do away with those ugly nested loops. Apart from the XPath he suggested, I'd probably go for that approach:

foreach ($dom->xpath('//Name[@lang]') as $name)
    $result[(string) $name['lang']] = (string) $name;
Sign up to request clarification or add additional context in comments.

Comments

1

I simply use xpath since I find that easier. With [@lang] only Name nodes with attribute lang will be selected. Edit: Since that was just an excerpt, I made it //Names so it will also be found if it is not root.

$test = <<< EOF
<Names>
<Name lang="en">Soccer</Name>
<Name lang="it">Calcio</Name>
</Names>
EOF;

$xml = simplexml_load_string($test);
$arr = array();
foreach ($xml->xpath("//Names/Name[@lang]") as $node) {
    $arr[(string) $node["lang"]] = (string) $node;
}
var_dump($arr);

Output:

array(2) {
  ["en"]=>
  string(6) "Soccer"
  ["it"]=>
  string(6) "Calcio"
}

4 Comments

Good idea using xPath, but wouldn't $xml->xpath('//Name[@lang]') make more sense here? Anyway +1 for xpath, hope you don't mind my adding it to my answer
@Elias Van Ootegem They are the same as long as it's always just /Names/Name and no other nodes. If there are other nodes and they contain Name then //Name might include too many hits.
That's exactly why I would use //Name[@lang]: aprat from the rather short XML snippet the OP has given us, we don't really know what the actual data looks like. A broad xpath might include too many nodes, but I'd rather have too much data than too little. Ah well, down to the OP to decide
Then I'd rather use //Names/Name tbh

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.