добавление пространства имен при использовании SimpleXMLElement - PullRequest
14 голосов
/ 03 августа 2011

Это то, что я после

<!-- language: lang-xml -->
<ws:Test>
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make>
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
    </ws:make>
</ws:Test>

Это мой текущий код

<!-- language: lang-php -->
$xmlTest = new SimpleXMLElement('<Test/>', 0, false, 'ws', true);
$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'ws');
#$make->addAttribute('name','Ford');
$make->addChild('ws:model', 'foo', 'ws');
$make->addChild('ws:model', 'bar', 'ws');
header ("Content-Type:text/xml");
print_r($xmlTest->asXML());

, но он выводит

<!-- language: lang-xml -->
<Test>
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make>
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
    </ws:make>
</Test>

Как вы можете видеть ш: отсутствует в тесте

Ответы [ 3 ]

30 голосов
/ 03 августа 2011

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

Тем не менее, я использовал обходной путь, чтобы в основном префикс префикса, так что анализатор удаляет только первые и оставляет второй

$xmlTest = new SimpleXMLElement('<xmlns:ws:Test></xmlns:ws:Test>', LIBXML_NOERROR, false, 'ws', true);
$xmlTest->addAttribute('xmlns:xmlns:ws', 'http://url.to.namespace');
$xmlTest->addAttribute('xmlns:xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');

Мне кажется, это работает, хотя мне интересно понять, почему SimpleXML делает это именно так.

9 голосов
/ 22 апреля 2017

Проблема

Проблема с этим кодом находится в самой первой строке:

$xmlTest = new SimpleXMLElement('<Test/>', 0, false, 'ws', true);

Прежде чем делать что-либо еще, давайте выведем это в виде XML:

echo $xmlTest->asXML();

<?xml version="1.0"?>
<Test/>

Это имеет смысл, мы получили то, что вкладываем.

Руководство довольно расплывчато в том, что делает аргумент $ns, но в этом случае оно не делает ничего полезного.Он устанавливает контекст для чтения XML, так что ->foo относится к <ws:foo>, а ['bar'] относится к ws:bar="...".Он ничего не делает для изменения структуры самого XML.

Установка корневого пространства имен

Чтобы установить пространство имен в корневом элементе, мы просто должны включить его в нашу строку, определяющуюкорневой элемент:

$xmlTest = new SimpleXMLElement('<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/" />');
echo $xmlTest->asXML();

<?xml version="1.0"?>
<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/"/>

Пока все хорошо ...

Настройка пространств имен для дочерних элементов

Далее, давайте рассмотрим, что на самом деле выводит код в вопросе (Я добавил несколько пробелов, чтобы сделать его более читабельным):

<?xml version="1.0"?> 
<Test>
   <ws:somename2 xmlns:ws="http://microsoft.com/wsdl/types/">somevalue2</ws:somename2>
   <ws:make xmlns:ws="ws">
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
   </ws:make>
</Test>

ОК, поэтому этот документ содержит два объявления пространства имен:

  • xmlns:ws="http://microsoft.com/wsdl/types/" в элементе somename2
  • xmlns:ws="ws" на элементе make, который затем наследуется элементами model

Что произойдет, если мы добавим наш исправленный корневой элемент?

<?xml version="1.0"?> 
<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/">
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make xmlns:ws="ws">
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
   </ws:make>
</ws:Test>

Классно, поэтому элемент somename2 теперь наследует свое определение пространства имен от корневого элемента и не объявляет его повторно.Но что не так с make и model с?Давайте сравним:

$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'ws');

Этот третий аргумент должен быть URI пространства имен , а не просто префиксом.Поэтому, когда мы задали его как 'ws', SimpleXML предположил, что мы хотим объявить фактический URI пространства имен как ws, поэтому добавил для этого атрибут xmlns.

Что мы на самом деле хотели, так это все элементычтобы быть в одном и том же пространстве имен:

$xmlTest = new SimpleXMLElement('<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/" />');
$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'http://microsoft.com/wsdl/types/');
#$make->addAttribute('name','Ford', 'http://microsoft.com/wsdl/types/');
$make->addChild('ws:model', 'foo', 'http://microsoft.com/wsdl/types/');
$make->addChild('ws:model', 'bar', 'http://microsoft.com/wsdl/types/');
echo $xmlTest->asXML();

<?xml version="1.0"?> 
<ws:Test xmlns:ws="http://microsoft.com/wsdl/types/">
    <ws:somename2>somevalue2</ws:somename2>
    <ws:make>
        <ws:model>foo</ws:model>
        <ws:model>bar</ws:model>
   </ws:make>
</ws:Test>

Отлично, мы получили желаемый результат!

Убираем

Но этот код выглядит довольно некрасиво, почему мынужно повторять URI везде?Что касается SimpleXML, выбор невелик: один и тот же префикс может означать разные вещи в разных частях документа, поэтому мы должны сказать ему, что мы хотим.

Что мы можем do приводит в порядок наш код, используя переменную или константу для URI пространства имен, вместо того, чтобы записывать его полностью каждый раз:

define('XMLNS_WS', 'http://microsoft.com/wsdl/types/');

$xmlTest = new SimpleXMLElement('<ws:Test xmlns:ws="' . XMLNS_WS . '" />');
$xmlTest->addChild("ws:somename2", "somevalue2", XMLNS_WS);
$make = $xmlTest->addChild('ws:make', null, XMLNS_WS);
#$make->addAttribute('name','Ford', XMLNS_WS);
$make->addChild('ws:model', 'foo', XMLNS_WS);
$make->addChild('ws:model', 'bar', XMLNS_WS);

Здесь нет ничего особенного в имени XMLNS_WS, ив равной степени это может быть переменная, константа класса или константа пространства имен.Код работает точно так же, на глаз немного проще.

0 голосов
/ 13 мая 2016
$xmlTest = new \SimpleXMLElement('<ws:Test></ws:Test>', LIBXML_NOERROR, false, 'ws', true);
$xmlTest->addAttribute('xmlns:xmlns:ws', 'http://url.to.namespace');
$xmlTest->addChild("ws:somename2", "somevalue2", 'http://microsoft.com/wsdl/types/');
$make = $xmlTest->addChild('ws:make', null, 'ws');
#$make->addAttribute('name','Ford');
$make->addChild('ws:model', 'foo', 'ws');
$make->addChild('ws:model', 'bar', 'ws');
...