【问题标题】:How to edit large XML files in PHP based on a record in the XML Node如何根据 XML 节点中的记录在 PHP 中编辑大型 XML 文件
【发布时间】:2021-12-26 18:59:04
【问题描述】:

我正在尝试通过 PHP 修改 130mb+ XML 文件,因此它只显示子节点是特定值的结果。由于我们用于将 XML 导入我们网站的软件的限制,我正在尝试对此进行过滤。

示例:(模型数据)

<Items>
<Item>
  <Barcode>...</Barcode>
  <BrandCode>...</BrandCode>
  <Title>...</Title>
  <Content>...</Content>
  <ShowOnWebsite>false</BrandDescr>
</Item> 
<Item>
  <Barcode>...</Barcode>
  <BrandCode>...</BrandCode>
  <Title>...</Title>
  <Content>...</Content>
  <ShowOnWebsite>true</BrandDescr>
</Item> 
<Item>
  <Barcode>...</Barcode>
  <BrandCode>...</BrandCode>
  <Title>...</Title>
  <Content>...</Content>
  <ShowOnWebsite>false</BrandDescr>
</Item>
</Items>

期望的结果: 我想创建一个新的 XML 文件,其中仅包含子“ShowOnWebsite”为真的记录。

我遇到的问题 因为 XML 太大了,所以简单的解决方案(例如使用 SimpleXML 或将 XML 加载到正文中并编辑其中的节点)不起作用。因为它们都将整个文件读入内存太慢而且通常会失败。

我还查看了 prewk/xml-string-streamer (https://github.com/prewk/xml-string-streamer),它非常适合流式传输大型 XML 文件,因为它不会将它们放在内存中,尽管我找不到任何修改XML 通过该解决方案。 (其他在线帖子说您需要在内存中拥有节点才能对其进行编辑)。

有人知道如何解决这个问题吗?

【问题讨论】:

    标签: php xml dom xml-parsing


    【解决方案1】:

    目标

    期望的结果:我想创建一个新的 XML 文件,其中仅包含子“ShowOnWebsite”为真的记录。

    给定

    test.xml

    <Items>
    <Item>
      <Barcode>...</Barcode>
      <BrandCode>...</BrandCode>
      <Title>...</Title>
      <Content>...</Content>
      <ShowOnWebsite>false</ShowOnWebsite>
    </Item> 
    <Item>
      <Barcode>...</Barcode>
      <BrandCode>...</BrandCode>
      <Title>...</Title>
      <Content>...</Content>
      <ShowOnWebsite>true</ShowOnWebsite>
    </Item> 
    <Item>
      <Barcode>...</Barcode>
      <BrandCode>...</BrandCode>
      <Title>...</Title>
      <Content>...</Content>
      <ShowOnWebsite>false</ShowOnWebsite>
    </Item>
    </Items>
    

    代码

    这是我写的实现。 getItems 产生子元素,而无需立即将 xml 加载到内存中。

    function getItems($fileName) {
        if ($file = fopen($fileName, "r")) {
            $buffer = "";
            $active = false;
            while(!feof($file)) {
                $line = fgets($file);
                $line = trim(str_replace(["\r", "\n"], "", $line));
                if($line == "<Item>") {
                    $buffer .= $line;
                    $active = true;
                } elseif($line == "</Item>") {
                    $buffer .= $line;
                    $active = false;
                    yield new SimpleXMLElement($buffer);
                    $buffer = "";
                } elseif($active == true) {
                    $buffer .= $line;
                }
            }
            fclose($file);
        }   
    }
    
    $output = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><Items></Items>');
    foreach(getItems("test.xml") as $element)
    {
        if($element->ShowOnWebsite == "true") {
            $item = $output->addChild('Item');
            $item->addChild('Barcode', (string) $element->Barcode);
            $item->addChild('BrandCode', (string) $element->BrandCode);
            $item->addChild('Title', (string) $element->Title);
            $item->addChild('Content', (string) $element->Content);
            $item->addChild('ShowOnWebsite', $element->ShowOnWebsite);
        }
    }
    
    $fileName = __DIR__ . "/test_" . rand(100, 999999) . ".xml";
    $output->asXML($fileName);
    

    输出

    <?xml version="1.0" encoding="utf-8"?>
    <Items><Item><Barcode>...</Barcode><BrandCode>...</BrandCode><Title>...</Title><Content>...</Content><ShowOnWebsite>true</ShowOnWebsite></Item></Items>
    

    【讨论】:

      【解决方案2】:

      XMLReader 有一个 expand() 方法,但 XMLWriter 缺少对应的方法。所以我在FluentDOM中添加了一个XMLWriter::collapse()方法。

      这允许使用 XMLReader 读取 XML,将其扩展为 DOM,使用 DOM 方法过滤/操作它并使用 XMLWriter 将其写回:

      require __DIR__.'/../../vendor/autoload.php';
      
      // Create the target writer and add the root element
      $writer = new \FluentDOM\XMLWriter();
      $writer->openUri('php://stdout');
      $writer->setIndent(2);
      $writer->startDocument();
      $writer->startElement('Items');
      
      // load the source into a reader
      $reader = new \FluentDOM\XMLReader();
      $reader->open(getXMLAsURI());
      
      // iterate the Item elements - the iterator expands them into a DOM node
      foreach (new FluentDOM\XMLReader\SiblingIterator($reader, 'Item') as $item) {
        /** @var \FluentDOM\DOM\Element $item */
        // only "ShowOnWebsite = true"
        if ($item('ShowOnWebsite = "true"')) {
          // write expanded node to the output
          $writer->collapse($item);
        }
      }
      
      $writer->endElement();
      $writer->endDocument();
      
      function getXMLAsURI() {
        $xml = <<<'XML'
      <Items>
        <Item>
          <Barcode>...</Barcode>
          <BrandCode>...</BrandCode>
          <Title>...</Title>
          <Content>...</Content>
          <ShowOnWebsite>false</ShowOnWebsite>
        </Item> 
        <Item>
          <Barcode>...</Barcode>
          <BrandCode>...</BrandCode>
          <Title>...</Title>
          <Content>...</Content>
          <ShowOnWebsite>true</ShowOnWebsite>
        </Item> 
        <Item>
          <Barcode>...</Barcode>
          <BrandCode>...</BrandCode>
          <Title>...</Title>
          <Content>...</Content>
          <ShowOnWebsite>false</ShowOnWebsite>
        </Item>
      </Items>
      XML;
        return 'data://text/plain;base64,'.base64_encode($xml);
      }
      

      【讨论】:

        猜你喜欢
        • 2015-08-04
        • 2011-03-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多