【问题标题】:parser error : XML declaration allowed only at the start of the document解析器错误:仅在文档开头允许 XML 声明
【发布时间】:2015-02-12 19:07:37
【问题描述】:

我有一个包含多个声明的 xml 文件,如下所示

<?xml version="1.0" encoding="UTF-8"?>
<root>
 <node>
  <element1>Stefan</element1>
  <element2>42</element2>
  <element3>Shirt</element3>
  <element4>3000</element4>  
</node>
</root>

<?xml version="1.0" encoding="UTF-8"?>
<root>
 <node>
  <element1>Damon</element1>
  <element2>32</element2>
  <element3>Jeans</element3>
  <element4>4000</element4>  
</node>
</root>

当我尝试使用

加载 xml 时
$data = simplexml_load_file("testdoc.xml") or die("Error: Cannot create object");

然后它给了我以下错误

Warning: simplexml_load_file(): testdoc.xml:11: parser error : XML declaration allowed only at the start of the document in C:\xampp\htdocs\crea\services\testxml.php on line 3

Warning: simplexml_load_file(): <?xml version="1.0" encoding="UTF-8"?> in C:\xampp\htdocs\crea\services\testxml.php on line 3

Warning: simplexml_load_file(): ^ in C:\xampp\htdocs\crea\services\testxml.php on line 3

Warning: simplexml_load_file(): testdoc.xml:12: parser error : Extra content at the end of the document in C:\xampp\htdocs\crea\services\testxml.php on line 3

Warning: simplexml_load_file(): <root> in C:\xampp\htdocs\crea\services\testxml.php on line 3

Warning: simplexml_load_file(): ^ in C:\xampp\htdocs\crea\services\testxml.php on line 3
Error: Cannot create object

请告诉我如何解析此 xml 或如何将其拆分为多个 xml 文件以便我可以阅读。文件大小约为 1 GB。

【问题讨论】:

    标签: php xml


    【解决方案1】:

    第二行

    <?xml version="1.0" encoding="UTF-8"?>
    

    需要删除。任何文件中只允许有 1 个 xml 声明,并且必须是第一行。

    严格来说,您还需要有一个根元素(尽管我见过宽松的解析器)。只需用伪标签包装内容,使您的文件看起来像:

    <?xml version="1.0" encoding="UTF-8"?>
    <metaroot><!-- synthetic unique root, no semantics attached -->
        <root>
            <!-- ... -->
        </root>
        <root>
            <!-- ... -->
        </root>
    
        <!-- ... -->
    </metaroot>
    

    (非常)大文件的解决方案:

    使用sed 消除有问题的xml 声明,使用printf 添加单个xml 声明和一个唯一的根元素。一系列 bash 命令如下:

      printf "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<metaroot>\n" >out.xml
      sed '/<\?xml /d' in.xml >>out.xml
      printf "\n</metaroot>\n" >>out.xml
    

    in.xml 表示您的原始文件,out.xml 表示清除的结果。

    printf 打印单个 xml 声明和开始/结束标记。 sed 是一种逐行编辑文件的工具,根据正则表达式模式匹配执行操作。要匹配的模式是 xml 声明的开头 (&lt;\? xml),要执行的操作是删除该行。

    注意事项:

    • 命令中的反斜杠在它们出现的位置转义具有特殊语义的符号。
    • sed 也适用于 windows/macos。

    替代解决方案

    另一种选择是将文件拆分成单独的格式良好的文件(取自this SO answer

    csplit -z -f 'temp' -b 'out%03d.xml' in.xml '/<\?xml /' {*}
    

    生成名为out000.xmlout001.xml、...的文件 您至少应该知道已处理到输入文件中的单个文件数量的大小,以便自动编号安全(尽管您当然可以将输入文件的字节数作为大小,使用-b 'out%09d.xml' in上面的命令)。

    【讨论】:

    • 先生,该文件大约 1 GB,包含多个声明,以上只是其结构示例。
    • 您可以控制文件的生成吗?显然它由连接的格式良好的 xml 文件组成——它们可以通过剥离它们的第一行来进行预处理。
    • 我明白先生,告诉我如何在包含 1 gb 数据的 xml 文件中实现这些更改
    • 该文件来自数据馈送服务器,因此我无法控制 xml 的生成
    • 使用 printfsed 对文件进行后处理(删除有问题的 xml 声明并添加 metaroot 元素。添加到答案的 cli 示例。
    【解决方案2】:

    这不是有效的 XML。您将需要使用字符串函数来拆分它 - 或者更准确地说是逐个读取它。

    $xmlDeclaration = '<?xml version="1.0" encoding="UTF-8"?>';
    
    $file = new SplFileObject($filename, 'r');
    $file->setFlags(SplFileObject::SKIP_EMPTY);
    $buffer = '';
    foreach ($file as $line) {
      if (FALSE === strpos($line, $xmlDeclaration)) {
        $buffer .= $line; 
      } else {
        outputBuffer($buffer);
        $buffer = $line;
      }
    }
    outputBuffer($buffer);
    
    function outputBuffer($buffer) {
      if (!empty($buffer)) {
        $dom = new DOMDocument();
        $dom->loadXml($buffer);
        $xpath = new DOMXPath($dom);
        echo $xpath->evaluate('string(//element1)'), "\n";
      }
    }
    

    输出:

    Stefan
    Damon
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-06
      • 2013-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-19
      • 1970-01-01
      相关资源
      最近更新 更多