开发者

PHP parsing XML file with and without namespaces

开发者 https://www.devze.com 2022-12-23 19:56 出处:网络
I need to get a XML File into a Database. Thats not the problem. Cant read it, parse it and create some Objects to map to the DB. Problem is, that sometimes the XML File can contain namespaces and som

I need to get a XML File into a Database. Thats not the problem. Cant read it, parse it and create some Objects to map to the DB. Problem is, that sometimes the XML File can contain namespaces and sometimes not. Furtermore sometimes there is no namespace defined at all.

So what i first got was something like this:

<?xml version="1.0" encoding="UTF-8"?>
<struct xmlns:b="http://www.w3schools.com/test/">
<objects>
<object>
<node_1>value1</node_1>
<node_2>value2</node_2>
<node_3 iso_land="AFG"/>
<coords lat="12.00" long="13.00"/>
</object>
</objects>
</struct>

And the parsing:

$obj = new stdClass();
$nodes = array('node_1', 'node_2');

$t = $xml->xpath('/objects/object');    
    foreach($nodes AS $node) {  
        if($t[0]->$node) {
            $obj->$node = (string) $t[0]->$node;
        }
    }

Thats fine as long as there are no namespaces. Here comes the XML File with namespaces:

<?xml version="1.0" encoding="UTF-8"?>
<b:struct xmlns:b="http://www.w3schools.com/test/">
<b:objects>
<b:object>
<b:node_1>value1</b:node_1>
<b:node_2>value2</b:node_2>
<b:node_3 iso_land="AFG"/>
<b:coords lat="12.00" long="13.00"/>
</b:object>
</b:objects>
</b:struct>

I now came up with something like this:

$xml = simplexml_load_file("test.xml");
$namespaces = $xml->getNamespaces(TRUE); 
$ns = count($namespaces) ? 'a:' : ''; 
$xml->registerXPathNamespace("a", "http://www.w3schools.com/test/");

$nodes = array('node_1', 'node_2');

$obj = new stdClass();

foreach($nodes AS $node) {
    $t = $xml->xpath('/'.$ns.'objects/'.$ns.'object/'.$ns.$node);   
    if($t[0]) {
        $obj->$node = (string) $t[0];
    }开发者_如何学Python
}

$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object/'.$ns.'node_3');
if($t[0]) {
    $obj->iso_land = (string) $t[0]->attributes()->iso_land;
}    

$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object/'.$ns.'coords');
if($t[0]) {
    $obj->lat = (string) $t[0]->attributes()->lat;
    $obj->long = (string) $t[0]->attributes()->long;
}

That works with namespaces and without. But i feel that there must be a better way. Before that i could do something like this:

$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object');  
foreach($nodes AS $node) {  
    if($t[0]->$node) {
        $obj->$node = (string) $t[0]->$node;
    }
}

But that just wont work with namespaces.


You could make 'http://www.w3schools.com/test/' the default namespace. This way a:objectswould match regardless of whether the document says <a:objects> or <objects>.

If memory usage is not a issue you can even do it with a textual replacement, e.g.

$data = '<?xml version="1.0" encoding="UTF-8"?>
<struct xmlns:b="http://www.w3schools.com/test/">
  <objects>
    <object>
      <node_1>value1</node_1>
      <node_2>value2</node_2>
      <node_3 iso_land="AFG"/>
      <coords lat="12.00" long="13.00"/>
    </object>
  </objects>
</struct>';

$data = str_replace( // or preg_replace(,,,1) if you want to limit it to only one replacement
  'xmlns:b="http://www.w3schools.com/test/"',
  'xmlns="http://www.w3schools.com/test/" xmlns:b="http://www.w3schools.com/test/"',
  $data
);
$xml = new SimpleXMLElement($data);
$xml->registerXPathNamespace("a", "http://www.w3schools.com/test/");

foreach($xml->xpath('//a:objects/a:object') as $n) {
  echo $n->node_1;
}


You can make your XPATH statements more generic by matching on any element * and using a predicate filter to match on the local-name(), which will match on the element name with/without namespaces.

An XPATH like this:

/*[local-name()='struct']/*[local-name()='objects']/*[local-name()='object']/*[local-name()='coords']

Applied to the code sample you were using:

$obj = new stdClass();
$nodes = array('node_1', 'node_2');

$t = $xml->xpath('/*[local-name()="objects"]/*[local-name()="object"]');    
    foreach($nodes AS $node) {  
        if($t[0]->$node) {
            $obj->$node = (string) $t[0]->$node;
        }
    }


Take a look at This http://blog.sherifmansour.com/?p=302 It helped me a lot.

0

精彩评论

暂无评论...
验证码 换一张
取 消