注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

phperwuhan的博客

记载一个phper的历程!phperwuhan.blog.163.com

 
 
 

日志

 
 

PHP doesn't like namespaces (that much)  

2008-09-03 16:14:23|  分类: php->xml |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

来源: http://people.ischool.berkeley.edu/~felix/xml/php-and-xmlns.html

The Problem

Assume an example XML file doc.xml:

<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://my.name/space">
  <nested>one</nested>
  <nested>two</nested>
</root>

It declares a default namespace, and thus all elements have qualified names that consist of (http://my.name/space, local-name).

XSLT

In XSLT, we can select the nodeset that contains all nested elements like that:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://my.name/space">
  ...
  <xsl:value-of select="/root/nested" />
  ...
</stylesheet>

DOMXpath

If we want to do the same using DOMXpath, we might think that this does what we want:

$doc = DOMDocument::load("doc.xml");
$xpath = new DOMXPath($doc);
foreach ( $xpath->query('/root/nested', $doc) as $node ) {
  echo $node->nodeValue;
}

But it doesn't.

We have to tell the PHP that we are using namespaces. This is done through $xpath->registerNamespace(prefix, namespace-URI). But now it comes: You can't register a default namespace with PHP's DOMXpath. You can neither write:

$xpath->registerNamespace("http://my.name/space");

...nor:

$xpath->registerNamespace("", "http://my.name/space");

You have to register some prefix. Even if the corresponding namespace is the default namespace elsewhere (e.g., in the XML document). After registering a prefix, the XPath doesn't work any longer, of course:

$xpath->registerNamespace("m", "http://my.name/space");
foreach ( $xpath->query('/root/nested', $doc) as $node ) {
  echo $node->nodeValue;
}

The XPath needs to access the XML nodes using the prefix as well. Finally, it works:

$doc = DOMDocument::load("doc.xml");
$xpath = new DOMXPath($doc);
$xpath->registerNamespace("m", "http://my.name/space");
foreach ( $xpath->query('/m:root/m:nested', $doc) as $node ) {
  echo $node->nodeValue;
}

Note that you don't have to change anything in the XML document (doc.xml in the above example).

Nothing really changes; the elements still are fully qualified, only the notation is different: Whereas we use a default namespace declaration for connecting the elements and the namespace URI in the XML document, we use a prefix declaration to achieve the same thing in PHP. Keep in mind that default namespaces are not different from namespaces bound to prefixes. It's just a shorthand notation. No matter whether we use this shorthand notation or prefixes: The namespaces are the same, and so are the qualified names.

Let's write down the example in a different way, then this maybe becomes more obvious. Remember the tupel story? If we expand the qualified names and write them down in Clark notation, both the XML document, the XSLT, and the PHP XPath query look the same:

<?xml version="1.0" encoding="utf-8"?>
<{http://my.name/space}root>
  <{http://my.name/space}nested>
    one
  </{http://my.name/space}nested>
  <{http://my.name/space}nested>
    two
  </{http://my.name/space}nested>
</{http://my.name/space}root>
<xsl:value-of
  select="/{http://my.name/space}root/{http://my.name/space}nested"
/>
foreach (
  $xpath->query('/{http://my.name/space}root/{http://my.name/space}nested', $doc)
  as $node ) {
  echo $node->nodeValue;
}

It's arguable, though, whether the absence of default namespaces is a bug or a missing feature or just not-so-beautiful in PHP.

SimpleXMLElement

I think it's the same for SimpleXML, where you have to use registerXPathNamespace in the same manner (registering a prefix even for what you wanted to be the default namespace).

by the way...

If you like, you can circumvent the problems caused by prefixes and default namespaces by writing XPath expressions in a more robust (and extremely ugly) way. Instead of binding http://my.name/space to prefix p and writing:

/p:root/p:nested
...you could write:
/*[namespace-uri() = 'http://my.name/space'][local-name() eq 'root']/*[namespace-uri() = 'http://my.name/space'][local-name() eq 'nested']

Nice, isn't it? Right, I wouldn't do it. But it rules out all dependencies on prefixes.

 

  评论这张
 
阅读(391)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017