【问题标题】:Unable to Parse XML having multiple Namespace Using PHP无法使用 PHP 解析具有多个命名空间的 XML
【发布时间】:2021-11-23 02:50:29
【问题描述】:

我正在通过 simplexml_load_file 函数从 php 解析 XML 文件。他们有多个命名空间。

    <?xml version="1.0" encoding="utf-8"?>
    <RETURN:RT xmlns:FORMM="http://example.com/5"
        xmlns:Form="http://example.com/master"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:RETURN="http://example.com">
   <FORMM:5F>
<Form:Info>
    <Form:SWCreatedBy>SW10002087</Form:SWCreatedBy>
</Form:Info>
</FORMM:5F>
</RETURN:RT>

这是我的 PHP 代码:

$xml = simplexml_load_file('filename');
        $namespaces = $xml->getNamespaces(true);
        foreach ($namespaces as $key =>  $extension) {
            
            $xml->registerXPathNamespace($key, $extension);
        }

         $main = $xml->xpath('//RETURN:RT/*');

        foreach ($main as $itr) {
           echo '<pre>';
            print_r($itr);
            echo '</pre>';
          }

输出:

 SimpleXMLElement Object
(
)

但是当我从命名空间中删除表单时,它会给我输出

SimpleXMLElement Object
(
    [Form:Info] => SimpleXMLElement Object
        (
            [Form:SWCreatedBy] => SW10002087
        )

)

任何人都可以给我解决方案,我不必每次都从 xml 文件中删除表单。

【问题讨论】:

标签: php xml


【解决方案1】:

5F 是无效的标签名称 - 它们不允许以数字开头。如果您加载此(无效)XML,解析器将抛出警告。

Warning: simplexml_load_string(): namespace error : Failed to parse QName 'FORMM:' in /in/HHTnS on line 33

Warning: simplexml_load_string():     <FORMM:5F> in /in/HHTnS on line 33

Warning: simplexml_load_string():            ^ in /in/HHTnS on line 33

因此您需要对此进行一些错误处理和/或修复 XML。

但是 LibXML(SimpleXML 和 DOM 背后的库)可以容忍它。所以你仍然可以阅读它。

不要从文档中获取命名空间。这是调试或通用转换的方法。命名空间 URI 是命名空间的标识值。命名空间前缀可以在 XML 中的任何元素处更改。为 Xpath 表达式定义/注册您自己的前缀,并在方法调用中使用命名空间 URI。

print_r(), var_dump(), ... 正在使用 XML 元素的 SimpleXMLs 映射。这是一种妥协并且有限制(尤其是命名空间)。尝试SimpleXMLElement::asXML() 调试SimpleXMLElement 实例。

您仍然可以使用属性语法访问元素,但您可能需要使用 SimpleXMLElement::children() 来指定命名空间。

以下是基于您的问题的示例:

$xml = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<RETURN:RT 
    xmlns:FORMM="http://example.com/5"
    xmlns:Form="http://example.com/master"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:RETURN="http://example.com">
    <FORMM:5F>
        <Form:Info>
            <Form:SWCreatedBy>SW10002087</Form:SWCreatedBy>
        </Form:Info>
   </FORMM:5F>
</RETURN:RT>
XML;

// define namespaces you're going to use
$xmlns = [
  'five' => 'http://example.com/5',
  'master' => 'http://example.com/master',
  'return' => 'http://example.com',
];

// a function to apply all of them to a SimpleXMLElement
function registerXpathNamespaces(SimpleXMLElement $target, array $xmlns) {
    foreach ($xmlns as $prefix => $namespaceURI) {
        $target->registerXpathNamespace($prefix, $namespaceURI);
    }
}

$rt = simplexml_load_string($xml);
registerXpathNamespaces($rt, $xmlns);
$list = $rt->xpath('//return:RT/*');
foreach ($list as $fiveF) {
    // serialize the node into a string for debugging
   var_dump($fiveF->asXML());
   // use the namespace list to access child elments in a specific namespace
   var_dump(
       (string)$fiveF->children($xmlns['master'])->Info->SWCreatedBy
   );
}

输出:

string(130) "<FORMM:5F>
        <Form:Info>
            <Form:SWCreatedBy>SW10002087</Form:SWCreatedBy>
        </Form:Info>
   </FORMM:5F>"
string(10) "SW10002087"

您需要在每个调用xpath()SimpleXMLElement 实例上注册命名空间。

在 DOM 中,这里有一个单独的 Xpath 对象,因此您只需注册一次命名空间。此外,DOMXpath::evaluate() 可以返回标量值(不仅仅是节点列表)。这就是示例在 DOM 中的外观:

// define namespaces you're going to use
$xmlns = [
  'five' => 'http://example.com/5',
  'master' => 'http://example.com/master',
  'return' => 'http://example.com',
];

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
foreach ($xmlns as $prefix => $namespaceURI) {
    $xpath->registerNamespace($prefix, $namespaceURI);
}

$list = $xpath->evaluate('//return:RT/*');
foreach ($list as $fiveF) {
    // serialize the node into a string for debugging
   var_dump($document->saveXML($fiveF));
   // directly fetch the value using a string cast in Xpath
   var_dump(
       $xpath->evaluate(
           'string(master:Info/master:SWCreatedBy)', 
           $fiveF
       )
   );
}

使用 DOM 你可以看看解析器如何读取损坏的标签。

// the RETURN:RT is valid - the parser can match it to the namespace
var_dump($document->documentElement->localName, $document->documentElement->namespaceURI);
foreach ($list as $fiveF) {
   // the "5F" or "FORMM:5F" is invalid - the parser will not match it
   var_dump($fiveF->localName, $fiveF->namespaceURI);
}

string(2) "RT"
string(18) "http://example.com"
string(8) "FORMM:5F"
NULL

因此它将别名/前缀保留为名称的一部分并忽略命名空间。

【讨论】:

  • 我想通过 from 上传 xml 文件到数据库。并且用户将上传它,所以我无权更改此 xml 文件
  • 解析器将容忍5F - 但它是无效的 XML。我修改了我的示例以使用损坏的 XML。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-26
  • 2014-06-14
  • 2016-09-22
  • 1970-01-01
  • 2012-08-15
  • 2011-01-30
相关资源
最近更新 更多