В SimpleXML есть объявление xmlns:xmlns= - нет возможности удалить

Так бесит, что я едва могу говорить. Я собрал RSS-канал с помощью SimpleXML, и все же — он использует пространства имен, которые есть прямо сейчас. Но он постоянно пытается объявить xmlns:xmlns="" в корневом узле при выводе. Хотя я ничего подобного не делаю.

Это начинается с

$rssXML->addAttribute("version", '2.0');
$rssXML->addAttribute("xmlns:media", "http://search.yahoo.com/mrss/", '');
$rssXML->addAttribute("xmlns:dcterms", "http://purl.org/dc/terms/", '');

и после этого я делаю: -

header("Content-Type: application/rss+xml");

echo $syndicationXML->asXML();

Тем не менее, он выводит: -

<?xml version="1.0"?>
<rss xmlns:xmlns="" version="2.0" xmlns:media="http://search.yahoo.com/mrss/" xmlns:dcterms="http://purl.org/dc/terms/"><channel>...

Я не понимаю всей этой декларации пространства имен. В чем дело?


person waxical    schedule 08.07.2011    source источник


Ответы (3)


Проблема с SimpleXML заключается в том, что эта функция addAttribute добавляет атрибут, а не пространство имен, и хотя кажется, что она делает то, что вы хотите, она не предназначена для использования так, как вы ее используете.

Он предназначен для добавления значения, являющегося частью определенного пространства имен (указанного в качестве третьего параметра), а не для добавления самого пространства имен. Причина, по которой вы получаете xmlns:xmlns, заключается в том, что SimpleXML обнаружил, что вы использовали пространство имен xmlns при указании имени xmlns:media, например, поэтому он создал пустое xmlns:xmlns.

Вот 2 решения вашей проблемы:

<сильный>1. Укажите в пространствах имен в конструкторе.

$rssXML = new SimpleXMLElement('<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:dcterms="http://purl.org/dc/terms/" />');
$rssXML->addAttribute('version', '2.0');

<сильный>2. Замените xmlns:xmlns="" на preg_replace

echo preg_replace('/xmlns:xmlns=""\s?/', '', $rssXML->asXML());
person Francois Deschenes    schedule 08.07.2011
comment
Очень четко объяснил, очень полезно и правильно. Спасибо. :) - person waxical; 08.07.2011

Вы написали, что хотите удалить это. Лучший способ — не ставить его на первое место.

Это работает с префиксом xmlns: (в другой раз) - он каким-то образом включает специальный режим работы в simplexml - и не предоставляет пустой URI пространства имен (иначе вы фактически попросите добавить это):

$rssXML = new SimpleXMLElement('<rss/>');
$rssXML->addAttribute("version", '2.0');
$rssXML->addAttribute("xmlns:xmlns:media", "http://search.yahoo.com/mrss/");
$rssXML->addAttribute("xmlns:xmlns:dcterms", "http://purl.org/dc/terms/");
$rssXML->asXML('php://output');

Это создает следующий вывод (украшенный для вашего удовольствия от чтения):

<?xml version="1.0"?>
<rss version="2.0" 
     xmlns:media="http://search.yahoo.com/mrss/" 
     xmlns:dcterms="http://purl.org/dc/terms/"/>

Этот «обман» изначально упоминался в "Невозможно добавить атрибут с префиксом пространства имен с помощью PHP Simplexml" а тут пропало.

person hakre    schedule 02.05.2013
comment
Это работает, потому что добавленный узел является атрибутом, а не объявлением пространства имен, а xmlns: является частью локальной части имени атрибута, а не префиксом пространства имен. Вы можете использовать любой префикс, а не только xmlns, так как он игнорируется. Это не имеет значения при сериализации, но имеет значение при работе с объектом (например, использование аргументов для SimpleXMLElement->attributes() и SimpleXMLElement->children() или аргументы пространства имен для SimpleXMLElement->addChild()). - person outis; 14.09.2016

SimpleXMLElement может добавлять пространства имен только косвенно. Поскольку вы можете добавлять только узлы элементов и атрибутов, но не объявления пространств имен, текстовые узлы или другие типы узлов, вы должны каким-то образом использовать SimpleXMLElement->addAttribute() или SimpleXMLElement->addChild(). Последний только добавит новые пространства имен дочернему элементу, поэтому здесь он бесполезен. addAttribute() добавит пространство имен вместе с атрибутом к данному элементу, поэтому, если вы затем удалите атрибут, у вас останется желаемое пространство имен.

Не существует очевидного метода удаления атрибута, но использование unset, как показано в нескольких ответах на "Удалить дочерний элемент с определенным атрибутом, в SimpleXML для PHP" можно адаптировать к задаче, используя SimpleXMLElement->attributes(), чтобы получить ссылку на атрибут.

<?php
$rssXML = new SimpleXMLElement('<rss/>');
$rssXML->addAttribute("version", '2.0');
# add a dummy attribute to get the namespace
$rssXML->addAttribute("media:_", '', "http://search.yahoo.com/mrss/");
unset($rssXML->attributes('media', TRUE)[0]);
# and again
$rssXML->addAttribute("dcterms:_", '', "http://purl.org/dc/terms/");
unset($rssXML->attributes('dcterms', TRUE)[0]);

echo $rssXML->asXML(), "\n";

Результат:

<?xml version="1.0"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:dcterms="http://purl.org/dc/terms/" version="2.0"/>

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

var_export($rssXML->getDocNamespaces());

Результат:

array (
  'media' => 'http://search.yahoo.com/mrss/',
  'dcterms' => 'http://purl.org/dc/terms/',
)
person outis    schedule 15.09.2016