Это то, что я после
<!-- 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
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');