识别 XML 元素并使用 PHP 将父节点复制到外部 XML 文件

Identify XML element and copy parent node to external XML file using PHP

我正在解析 PHP 中的 XML 文件 (source.xml),需要识别 <property> 节点包含 <rent> 元素的实例。

一旦确定该条目的整个 <property> 父节点,应将其复制到单独的 XML 文件 (destination.xml)。

复制完成后,<property> 节点应从 source.xml 文件中删除。

这是 source.xml 文件的示例:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <property>
        ...
        <rent>
            <term>long</term>
            <freq>month</freq>
            <price_peak>1234</price_peak>
            <price_high>1234</price_high>
            <price_medium>1234</price_medium>
            <price_low>1234</price_low>
        </rent>
        ...
    </property>
</root>

我已经尝试将 DOM 与以下代码一起使用,但是尽管它们有数百个节点符合上述要求,但我根本没有得到任何结果。这是我目前所拥有的:

$destination = new DOMDocument;
$destination->preserveWhiteSpace = true;
$destination->load('destination.xml');

$source = new DOMDocument;
$source->load('source.xml');

$xp = new DOMXPath($source);

foreach ($xp->query('/root/property/rent[term/freq/price_peak/price_high/price_medium/price_low]') as $item) {

    $newItem = $destination->documentElement->appendChild(

        $destination->createElement('property')

    );

    foreach (array('term', 'freq', 'price_peak', 'price_high', 'price_medium', 'price_low') as $elementName) {

        $newItem->appendChild(
            $destination->importNode(
                $item->getElementsByTagName($elementName)->property(0),
                true
            )
        );
    }
}

$destination->formatOutput = true;
echo $destination->saveXml();

我才刚刚开始了解 DOMDocument 及其用途,所以我显然在某个地方搞砸了,因此我们将不胜感激。非常感谢。

困难在于当您试图将一个节点从一个文档复制到另一个文档时。您可以尝试重新创建节点,复制所有组件,但这是一项艰巨的工作(并且容易出错)。相反,您可以使用 importNode 将节点从一个文档导入到另一个文档。第二个参数表示也复制所有子元素。

然后从原始文档中删除元素就是让项目“从其父项中删除自身”的情况,这听起来很奇怪,但这就是这段代码的工作原理。

<?php
error_reporting ( E_ALL );
ini_set ( 'display_errors', 1 );

$destination = new DOMDocument;
$destination->preserveWhiteSpace = true;
$destination->loadXML('<?xml version="1.0" encoding="utf-8"?><root></root>');

$source = new DOMDocument;
$source->load('NewFile.xml');

$xp = new DOMXPath($source);
$destRoot = $destination->getElementsByTagName("root")->item(0);
foreach ($xp->query('/root/property[rent]') as $item) {
    $newItem = $destination->importNode($item, true);
    $destRoot->appendChild($newItem);
    $item->parentNode->removeChild($item);
}

echo "Source:".$source->saveXML();
$destination->formatOutput = true;
echo "destination:".$destination->saveXml();

对于目的地,我用基本 <root> 元素填充它,然后从那里添加内容。

你是不是也想获得这样的东西?希望这会有所帮助:

$inXmlFile = getcwd() . "/source.xml";
$inXmlString = file_get_contents($inXmlFile);
$outXmlFile = getcwd() . "/destination.xml";
$outXmlString = file_get_contents($outXmlFile);

$sourceDOMDocument = new DOMDocument;
$sourceDOMDocument->loadXML($inXmlString);

$sourceRoot = null;
foreach ($sourceDOMDocument->childNodes as $childNode) {
    if(strcmp($childNode->nodeName, "root") == 0) {
        $sourceRoot = $childNode;
        break;
    }
}

$destDOMDocument = new DOMDocument;
$destDOMDocument->loadXML($outXmlString);

$destRoot = null;
foreach ($destDOMDocument->childNodes as $childNode) {
    if(strcmp($childNode->nodeName, "root") == 0) {
        $destRoot = $childNode;
        break;
    }
}

$xmlStructure = simplexml_load_string($inXmlString);

$domProperty = dom_import_simplexml($xmlStructure->property);

$rents = $domProperty->getElementsByTagName('rent');
if(($rents != null) && (count($rents) > 0)) {
    $destRoot->appendChild($destDOMDocument->importNode($domProperty->cloneNode(true), true));
    $destDOMDocument->save($outXmlFile);
    $sourceRoot->removeChild($sourceRoot->getElementsByTagName('property')->item(0));
    $sourceDOMDocument->save($inXmlFile);
}

考虑 运行 两种 XSLT 转换:一种在目标中添加 <property><rent> 节点,另一种从源中删除这些节点。作为背景,XSLT 是一种特殊用途的语言,旨在转换 XML 文件,甚至维护 document() 函数以从同一文件夹或子文件夹中的外部 XML 文件进行解析。

PHP 可以 运行 XSLT 1.0 脚本及其 php-xsl class(确保在 .ini 文件中启用扩展)。使用这种方法不需要 if 逻辑或 foreach 循环。

XSLT 脚本

PropertyRentAdd.xsl (确保 source.xml 和 XSLT 在同一文件夹中)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- IDENTITY TRANSFORM -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- ADD TEMPLATE -->
  <xsl:template match="root"> 
    <xsl:copy>
        <xsl:copy-of select="*"/>
        <xsl:copy-of select="document('source.xml')/root/property[local-name(*)='rent']"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

PropertyRentRemove.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <!-- IDENTITY TRANSFORM -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- REMOVE TEMPLATE -->
  <xsl:template match="property[local-name(*)='rent']">    
  </xsl:template>

</xsl:stylesheet>

PHP

// Set current path
$cd = dirname(__FILE__);

// Load the XML and XSLT files
$doc = new DOMDocument();
$doc->load($cd.'/destination.xml');     

$xsl = new DOMDocument;
$xsl->load($cd.'/PropertRentAdd.xsl');

// Transform the destination xml
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);     
$newXml = $proc->transformToXML($xml);

// Save output to file, overwriting original
file_put_contents($cd.'/destination.xml', $newXml);


// Load the XML and XSLT files
$doc = new DOMDocument();
$doc->load($cd.'/source.xml');

$xsl = new DOMDocument;
$xsl->load($cd.'/PropertRentRemove.xsl');

// Transform the source xml
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);     
$newXml = $proc->transformToXML($xml);

// Save output overwriting original file
file_put_contents($cd.'/source.xml', $newXml);

Inputs (示例演示,与其他标签显示内容不受影响)

source.xml

<?xml version="1.0" encoding="utf-8"?>
<root>
    <property>
        <rent>
            <term>long</term>
            <freq>month</freq>
            <price_peak>1234</price_peak>
            <price_high>1234</price_high>
            <price_medium>1234</price_medium>
            <price_low>1234</price_low>
        </rent>
    </property>
    <property>
        <rent>
            <term>short</term>
            <freq>month</freq>
            <price_peak>7890</price_peak>
            <price_high>7890</price_high>
            <price_medium>7890</price_medium>
            <price_low>7890</price_low>
        </rent>
    </property>
    <property>
        <web_site>Whosebug</web_site>
        <general_purpose>php</general_purpose>
    </property>
    <property>
        <web_site>Whosebug</web_site>
        <special_purpose>xsl</special_purpose>
    </property>
</root>

destination.xml

<?xml version="1.0" encoding="utf-8"?>
<root>
    <original_data>
        <test1>ABC</test1>
        <test2>123</test2>
    </original_data>
    <original_data>
        <test1>XYZ</test1>
        <test2>789</test2>
    </original_data>
</root>

输出(在PHP运行之后)

source.xml

<?xml version="1.0"?>
<root>
  <property>
    <web_site>Whosebug</web_site>
    <general_purpose>php</general_purpose>
  </property>
  <property>
    <web_site>Whosebug</web_site>
    <special_purpose>xsl</special_purpose>
  </property>
</root>

destination.xml (底部追加新节点)

<?xml version="1.0"?>
<root>
  <original_data>
    <test1>ABC</test1>
    <test2>123</test2>
  </original_data>
  <original_data>
    <test1>XYZ</test1>
    <test2>789</test2>
  </original_data>
  <property>
    <rent>
      <term>long</term>
      <freq>month</freq>
      <price_peak>1234</price_peak>
      <price_high>1234</price_high>
      <price_medium>1234</price_medium>
      <price_low>1234</price_low>
    </rent>
  </property>
  <property>
    <rent>
      <term>short</term>
      <freq>month</freq>
      <price_peak>7890</price_peak>
      <price_high>7890</price_high>
      <price_medium>7890</price_medium>
      <price_low>7890</price_low>
    </rent>
  </property>
</root>