PHP-разбор XML-файла с пространствами имен и без них - PullRequest
0 голосов
/ 20 марта 2010

Мне нужно получить XML-файл в базу данных. Это не проблема. Не могу прочитать его, разобрать его и создать несколько объектов для сопоставления с БД. Проблема в том, что иногда XML-файл может содержать пространства имен, а иногда нет. Более того, иногда пространство имен вообще не определено.

Итак, что я впервые получил, было примерно так:

<?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>

И разбор:

$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;
        }
    }

Хорошо, если нет пространств имен. Вот XML-файл с пространствами имен:

<?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>

Я сейчас придумал что-то вроде этого:

$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];
    }
}

$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;
}

Это работает с пространствами имен и без. Но я чувствую, что должен быть лучший путь. До этого я мог сделать что-то вроде этого:

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

Но это просто не будет работать с пространствами имен.

Ответы [ 3 ]

1 голос
/ 20 марта 2010

Вы можете сделать 'http://www.w3schools.com/test/' пространством имен по умолчанию. Таким образом, a:objects будет соответствовать независимо от того, будет ли в документе указано или .

Если использование памяти не является проблемой, вы даже можете сделать это с текстовой заменой, например,

$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;
}
0 голосов
/ 20 декабря 2012

Взгляните на это http://blog.sherifmansour.com/?p=302 Это мне очень помогло.

0 голосов
/ 20 марта 2010

Вы можете сделать ваши операторы XPATH более общими, сопоставив их с любым элементом * и используя фильтр предикатов для сопоставления с local-name(), который будет сопоставляться по имени элемента с пространствами имен или без них.

XPATH, подобный этому:

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

Применяется к примеру кода, который вы использовали:

$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;
        }
    }
...