使用 PHP 从 XML 中删除命名空间
Posted
技术标签:
【中文标题】使用 PHP 从 XML 中删除命名空间【英文标题】:Remove namespace from XML using PHP 【发布时间】:2010-11-17 18:28:28 【问题描述】:我有一个如下所示的 XML 文档:
<Data
xmlns="http://www.domain.com/schema/data"
xmlns:dmd="http://www.domain.com/schema/data-metadata"
>
<Something>...</Something>
</Data>
我在 php 中使用 SimpleXML 解析信息。我正在处理数组,我似乎遇到了命名空间的问题。
我的问题是:如何删除这些命名空间?我从 XML 文件中读取数据。
谢谢!
【问题讨论】:
如果您想了解详细信息...我的原始问题已发布在这里,用户已经回答了(谢谢!)。但我发现命名空间导致他的循环不运行并返回一个空数组。原始问题位于此处:***.com/questions/1209301/… 【参考方案1】:如果您使用的是 XPath,那么这是 XPath 的限制,不是 PHP 请查看xpath and default namespaces 上的此说明以获取更多信息。
更具体地说,是根节点中的xmlns=""
属性导致了问题。这意味着您需要注册命名空间,然后使用QName 来引用元素。
$feed = simplexml_load_file('http://www.sitepoint.com/recent.rdf');
$feed->registerXPathNamespace("a", "http://www.domain.com/schema/data");
$result = $feed->xpath("a:Data/a:Something/...");
重要提示:registerXPathNamespace
调用中使用的 URI 必须与实际 XML 文件中使用的相同。
【讨论】:
对,所以不是删除...我只是注册命名空间。这解决了我的问题!!!你就是那个男人!谢谢! 不幸的是,这似乎是唯一的方法。 请注意重要部分。我第一次查看此答案时错过了这一点。【参考方案2】:我发现上面的答案很有帮助,但对我来说并不是很有效。 这最终效果更好:
// Gets rid of all namespace definitions
$xml_string = preg_replace('/xmlns[^=]*="[^"]*"/i', '', $xml_string);
// Gets rid of all namespace references
$xml_string = preg_replace('/[a-zA-Z]+:([a-zA-Z]+[=>])/', '$1', $xml_string);
【讨论】:
我会用这样的东西摆脱“所有命名空间引用”: $xml = preg_replace('/(:]+:/', '$1' , $xml); 我一生中为数不多的几次投票支持使用正则表达式操作 XML 的解决方案。我真的不想注册一个默认命名空间并不必要地弄乱我的 xpath 查询。 几乎完美。需要在节点名称后寻找潜在的空间。如果节点内容有冒号<node>Order:Num</node>
,则剥离节点内容,也找不到数字键<ns:addr2>Content</ns:addr2>
。试试看:$xml_string = preg_replace('/(<\/|<)[a-zA-Z]+:([a-zA-Z0-9]+[ =>])/', '$1$2', $xml_string);
【参考方案3】:
以下 PHP 代码自动检测 XML 文件中以别名“default”指定的默认命名空间。无需更新所有 xpath 查询以包含前缀 default:
因此,如果您想读取 XML 文件而不是它们包含默认的 NS 定义或者它们不包含并且您想查询所有 Something
元素,您可以使用以下代码:
$xml = simplexml_load_file($name);
$namespaces = $xml->getDocNamespaces();
if (isset($namespaces['']))
$defaultNamespaceUrl = $namespaces[''];
$xml->registerXPathNamespace('default', $defaultNamespaceUrl);
$nsprefix = 'default:';
else
$nsprefix = '';
$somethings = $xml->xpath('//'.$nsprefix.'Something');
echo count($somethings).' times found';
【讨论】:
【参考方案4】:当您只希望您的 xml 被解析后被使用,并且您不关心任何命名空间时, 你只需删除它们。正则表达式很好,而且比我下面的方法快。
但是为了在删除命名空间时更安全,可以使用 SimpleXML 解析 xml 并询问它具有的命名空间,如下所示:
$xml = '...';
$namespaces = simplexml_load_string($xml)->getDocNamespaces(true);
//The line bellow fetches default namespace with empty key, like this: '' => 'url'
//So we remove any default namespace from the array
$namespaces = array_filter(array_keys($namespaces), function($k)return !empty($k););
$namespaces = array_map(function($ns)return "$ns:";, $namespaces);
$ns_clean_xml = str_replace("xmlns=", "ns=", $xml);
$ns_clean_xml = str_replace($namespaces, array_fill(0, count($namespaces), ''), $ns_clean_xml);
$xml_obj = simplexml_load_string($ns_clean_xml);
因此,您只为命名空间点击替换,避免删除 xml 可能拥有的任何其他内容。
其实我是用它作为方法的:
function refined_simplexml_load_string($xml_string)
if(false === ($x1 = simplexml_load_string($xml_string)) ) return false;
$namespaces = array_keys($x1->getDocNamespaces(true));
$namespaces = array_filter($namespaces, function($k)return !empty($k););
$namespaces = array_map(function($ns)return "$ns:";, $namespaces);
return simplexml_load_string($ns_clean_xml = str_replace(
array_merge(["xmlns="], $namespaces),
array_merge(["ns="], array_fill(0, count($namespaces), '')),
$xml_string
));
【讨论】:
【参考方案5】:要完全删除命名空间,您需要使用正则表达式 (RegEx)。例如:
$feed = file_get_contents("http://www.sitepoint.com/recent.rdf");
$feed = preg_replace("/<.*(xmlns *= *[\"'].[^\"']*[\"']).[^>]*>/i", "", $feed); // This removes ALL default namespaces.
$xml_feed = simplexml_load_string($feed);
然后您在加载 XML 之前剥离了所有 xml 命名空间(请小心使用正则表达式,因为如果您有任何字段,例如:
<![CDATA[ <Transfer xmlns="http://redeux.example.com">cool.</Transfer> ]]>
然后它将从 CDATA 中剥离 xmlns,这可能会导致意外结果。
【讨论】:
不错,但它不会删除结束标签以上是关于使用 PHP 从 XML 中删除命名空间的主要内容,如果未能解决你的问题,请参考以下文章