Удалить пространство имен из XML с помощью PHP

У меня есть XML-документ, который выглядит так:

<Data 
  xmlns="http://www.domain.com/schema/data" 
  xmlns:dmd="http://www.domain.com/schema/data-metadata"
>
  <Something>...</Something>
</Data>

Я анализирую информацию, используя SimpleXML в PHP. Я имею дело с массивами, и у меня, кажется, проблема с пространством имен.

Мой вопрос: как удалить эти пространства имен? Я читаю данные из файла XML.

Спасибо!


person jchimpo    schedule 07.08.2009    source источник
comment
Если вам нужны подробности... здесь был опубликован мой первоначальный вопрос, на который уже ответил пользователь (Спасибо!). Но я обнаружил, что пространство имен заставляет его циклы не запускаться и возвращать пустой массив. Исходный вопрос находится здесь: stackoverflow.com/ вопросы/1209301/   -  person jchimpo    schedule 07.08.2009


Ответы (5)


Если вы используете XPath, то это ограничение XPath, а не PHP. Посмотрите это объяснение на xpath и пространства имен по умолчанию для получения дополнительной информации.

Точнее, это атрибут xmlns="" в корневом узле, который вызывает проблему. Это означает, что вам необходимо зарегистрировать пространство имен, а затем использовать QName для ссылки на элементы.

$feed = simplexml_load_file('http://www.sitepoint.com/recent.rdf');
$feed->registerXPathNamespace("a", "http://www.domain.com/schema/data");
$result = $feed->xpath("a:Data/a:Something/...");

Важно: URI, используемый в вызове registerXPathNamespace, должен быть идентичен тому, который используется в фактическом файле XML.

person null    schedule 07.08.2009
comment
Итак, вместо удаления... я просто регистрирую пространство имен. И это решило мою проблему!!! Ты мужчина! Спасибо! - person jchimpo; 07.08.2009
comment
К сожалению, это, кажется, единственный путь. - person Alexei Tenitski; 03.12.2009
comment
Обратите внимание на раздел Важно. Я пропустил это, когда впервые просмотрел этот ответ. - person Kevin Schroeder; 17.03.2017

Я нашел ответ выше полезным, но он не совсем сработал для меня. В итоге это сработало лучше:

// Gets rid of all namespace definitions 
$xml_string = preg_replace('/xmlns[^=]*="[^"]*"/i', '', $xml_string);

// Gets rid of all namespace references
$xml_string = preg_replace('/[a-zA-Z]+:([a-zA-Z]+[=>])/', '$1', $xml_string);
person Chris Lawrence    schedule 03.10.2011
comment
Я бы избавился от всех ссылок на пространство имен примерно так: $xml = preg_replace('/(‹\/*)[^›:]+:/', '$1', $xml); - person Silas Palmer; 28.08.2014
comment
Один из немногих раз в моей жизни, когда я проголосовал за решение для управления XML с помощью регулярных выражений. Я действительно не хочу регистрировать пространство имен по умолчанию и бесполезно загромождать мои запросы xpath. - person billynoah; 28.11.2018
comment
Практически идеально. Необходимо искать потенциальный пробел после имени узла. Удаляет содержимое узла, если оно содержит двоеточие <node>Order:Num</node>, а также не находит числовые клавиши <ns:addr2>Content</ns:addr2>. Попробуйте: $xml_string = preg_replace('/(<\/|<)[a-zA-Z]+:([a-zA-Z0-9]+[ =>])/', '$1$2', $xml_string); - person M P; 07.08.2019

Следующий PHP-код автоматически определяет пространство имен по умолчанию, указанное в XML-файле под псевдонимом «default». Нет необходимости обновлять все запросы xpath для включения префикса default:.

Итак, если вы хотите читать XML-файлы, а не содержат определение NS по умолчанию или нет, и вы хотите запросить все элементы Something, вы можете использовать следующий код:

$xml = simplexml_load_file($name);
$namespaces = $xml->getDocNamespaces();
if (isset($namespaces[''])) {
    $defaultNamespaceUrl = $namespaces[''];
    $xml->registerXPathNamespace('default', $defaultNamespaceUrl);
    $nsprefix = 'default:';
} else {
    $nsprefix = '';
}

$somethings = $xml->xpath('//'.$nsprefix.'Something');

echo count($somethings).' times found';
person Alex    schedule 30.10.2012

Чтобы полностью удалить пространство имен, вам нужно использовать регулярные выражения (RegEx). Например:

$feed = file_get_contents("http://www.sitepoint.com/recent.rdf");
$feed = preg_replace("/<.*(xmlns *= *[\"'].[^\"']*[\"']).[^>]*>/i", "", $feed); // This removes ALL default namespaces.
$xml_feed = simplexml_load_string($feed);

Затем вы удалили все пространства имен xml перед загрузкой XML (будьте осторожны с регулярным выражением, потому что если у вас есть какие-либо поля с чем-то вроде:

<![CDATA[ <Transfer xmlns="http://redeux.example.com">cool.</Transfer> ]]>

Затем он удалит xmlns из CDATA, что может привести к неожиданным результатам.

person null    schedule 07.08.2009
comment
Хорошо, но не удаляет закрывающий тег - person tnt-rox; 26.07.2013

Когда вы просто хотите, чтобы ваш xml, проанализированный, использовался, и вам не нужны какие-либо пространства имен, вы просто удаляете их. Регулярные выражения хороши и намного быстрее, чем мой метод ниже.

Но для более безопасного подхода при удалении пространств имен можно проанализировать xml с помощью SimpleXML и запросить имеющиеся у него пространства имен, как показано ниже:

$xml = '...';
$namespaces = simplexml_load_string($xml)->getDocNamespaces(true);
//The line bellow fetches default namespace with empty key, like this: '' => 'url'
//So we remove any default namespace from the array
$namespaces = array_filter(array_keys($namespaces), function($k){return !empty($k);});
$namespaces = array_map(function($ns){return "$ns:";}, $namespaces);

$ns_clean_xml = str_replace("xmlns=", "ns=", $xml);
$ns_clean_xml = str_replace($namespaces, array_fill(0, count($namespaces), ''), $ns_clean_xml);
$xml_obj = simplexml_load_string($ns_clean_xml);

Таким образом, вы нажимаете заменить только для пространств имен, избегая удаления всего, что может быть в xml.

На самом деле я использую его как метод:

function refined_simplexml_load_string($xml_string) {
  if(false === ($x1 = simplexml_load_string($xml_string)) ) return false;
  
  $namespaces = array_keys($x1->getDocNamespaces(true));
  $namespaces = array_filter($namespaces, function($k){return !empty($k);});
  $namespaces = array_map(function($ns){return "$ns:";}, $namespaces);
  
  return simplexml_load_string($ns_clean_xml = str_replace(
    array_merge(["xmlns="], $namespaces),
    array_merge(["ns="], array_fill(0, count($namespaces), '')),
    $xml_string
  ));
}
person AlexisAmasis    schedule 07.02.2021