给定一些类似下面的XML,如何从每个元素中完全删除特定的名称空间,包括其声明?
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns:my-co="http://www.example.com/2015/co">
<my-namespace:first xmlns:my-namespace="http://www.example.com/2015/ns">
<element my-namespace:id="1">
</element>
</my-namespace:first>
<second>
<my-namespace:element xmlns:my-namespace="http://www.example.com/2015/ns" my-co:id="2">
</my-namespace:element>
</second>
</document>
注意,根级别没有xmlns:my-namespace
声明,这两个声明位于XML结构的不同部分和级别。
如何高效地只删除名称空间my-namespace
,而不必检查代码中的每个节点?
下面是XML应该看起来像:
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns:my-co="http://www.example.com/2015/co">
<first>
<element id="1">
</element>
</first>
<second>
<element my-co:id="2">
</element>
</second>
</document>
3条答案
按热度按时间tyky79it1#
下面的代码可以实现这个功能:
XPath查询查找所有具有名称空间节点
$ns
的节点,而其父节点不具有相同的名称空间。这将查找/document/my-namespace:first
和/document/second/my-namespace:element
,但不会查找/document/my-namespace:first/element
,因为其父节点也具有名称空间my-namespace
。然后,代码从找到的每个元素中删除指定的名称空间。从元素中删除命名空间也会自动将其从所有子元素中删除。许多真实的世界中的XML文档都在根元素上声明了
xmlns
,但是这段代码可以在任何地方处理它们。zbwhf8kr2#
我们也想移除名称空间(在我们的例子中是所有的名称空间,而不仅仅是一个特定的名称空间),但是上面的解决方案只起了部分作用。如果一个前缀被定义了多次,但是使用了不同的URI,第一个解决方案并没有移除所有的名称空间。
一个在所有用例中都适用的解决方案是使用
SimpleXMLElement
搜索名称空间,并使用SimpleXMLElement->xpath()
搜索该名称空间的节点,然后转换为DOMElement
以删除名称空间。对于我们来说,使用这种方法比在DOM中加载XML并使用DOMXPath
更好地管理内存。要测试的示例XML:
删除命名空间的示例代码:
重新创建
SimpleXMLElement
是因为在某些情况下,如果您在使用DOM删除名称空间后尝试访问或操作SimpleXMLElement
,PHP(5. 6)会因分段错误而崩溃。幸运的是,尽管asXML()
仍能正常工作,以允许此解决方案,因为新创建的对象不会导致崩溃。如果你想删除特定的命名空间,你可以重写函数和/或xpath,使其只搜索特定的命名空间,注意你还必须改变
SimpleXMLElement->getDocNamespaces(true, true)
的用法。补充说明:我们只查找第一个名称空间的第一个节点,然后出于性能原因尝试从该节点删除所有名称空间。我们有时不得不处理可能包含100多个不同名称空间并且可能有几MB大的可怕XML。为每个名称空间创建一个xpath在这些文档上非常慢。此解决方案极大地提高了性能,因为它假设大多数如果不是全部,则在同一元素中声明命名空间(通常是根元素)。因此,与其循环并分别为每个名称空间执行xpath,它只是尝试从文档中第一个命名空间的第一个元素中移除所有命名空间,然后重新检查是否还有命名空间,但是如果文档中稍后还有命名空间,它仍然会移除它们。如果名称空间在文档中分布得更广,那么一种不同的方法可能更好。
ercv8c1e3#
SimpleXML有一个提取所有名称空间信息的函数,DOMXML有一个删除它的函数,如果你知道要删除什么的话。
下面是一个从DOMDoc中提取名称空间信息的简单函数,方法是将其导入SimpleXMLElement,然后使用名称空间数组从DOMDoc中本机删除所有名称空间内容