добавление пространства имен при использовании SimpleXMLElement

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

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

Как вы видите, ws: отсутствует в Test

Solutions Collecting From Web of "добавление пространства имен при использовании SimpleXMLElement"

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 делает это именно так.

Проблема

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

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

Cool, поэтому элемент 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, выбора не так много: один и тот же префикс может означать разные вещи в разных частях документа, поэтому мы должны сказать, чего мы хотим.

То, что мы можем сделать, – это убрать наш код, используя переменную или константу для 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 , и в равной степени это может быть переменная, константа класса или константа пространства имен. Код работает точно так же, это немного легче на глаза.

 $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');