使用匹配的节点加入或合并两个XML文件

时间:2015-06-30 22:41:54

标签: php xml merge

如何使用php合并基于公共匹配节点(密钥或在此示例中为<id>)的2 xml记录?

1.XML:

<record>
   <id>001</id>
   <other_nodes>...</other_nodes>
   ...
</record>
<record>
   <id>002</id>
   ...
</record>

2.XML:

<record>
   <id>001</id>
   <description>abc</description>
   ...
</record>
<record>
   <id>002</id>
   <description>def</description>
   ...
    </record>

Merged.xml:

<record>
    <id>001</id>
    <other_nodes></other_nodes>
    <description>abc</description>
    ...
</record>
<record>
    <id>002</id>
    <description>def</description>
    ...
</record>

<id>旁边,两个xml文件中的所有节点都不同(唯一)。目的是将2.xml的附加到1.xml。 (只需将<description>节点添加到1.xml也可以完成这项工作!)

我尝试了几种编码(比如嵌套的foreach循环),没有任何效果。我能做的最好(我试过多个版本):

(灵感来自Merge two xml files based on common attribute):

$file = ...    
$targetDom = new DOMDocument();
$targetDom->load($file);
$targetXpath = new DOMXpath($targetDom);

$addDom = new DOMDocument();
$addDom->loadXml($file2);
$addXpath = new DOMXpath($addDom);

// copy elements depending on ProductId
foreach ($targetXpath->evaluate('//record') as $record) {
  $productId = $record->id->value;
  foreach ($addXpath->evaluate('//record[id=\"'.$productId.'\"]') as $attribute) {
    $parent_node = $attribute->evaluate('../codename'); //evaluate or xpath? 
    //$record->appendChild($targetDom->importNode($parent_node));
    $newValue = $attribute->description->value;

    //$record->addChild("description", $newValue);
    $element = $dom->createElement('codename', $newValue);
    $record->appendChild($element);

  //$targetDom->documentElement->appendChild(
    //  $targetDom->importNode($parent_node));  
  }
}

$xmlsave = $targetDom->saveXml();

相关问题: How to join two XML files with a matching node

1 个答案:

答案 0 :(得分:0)

您可以使用xsl(t)

执行此操作

merge.xsl

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:exsl="http://exslt.org/common"
    extension-element-prefixes="exsl"
>
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" indent="yes"/>
    <xsl:param name="mergedoc" select="document($mergesrc)" />

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

    <xsl:template match="record">
        <xsl:variable name="idmerge" select="id" />
        <xsl:copy>
            <xsl:apply-templates />
            <xsl:apply-templates select="$mergedoc/records/record[id=$idmerge]/*[not(name()='id')]" />
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

php脚本:

<?php
$src = doc('1.xml');
$processor = new XSLTProcessor;
$processor->importStylesheet( doc('merge.xsl') );
$processor->setParameter('', 'mergesrc', '2.xml');

$result = $processor->transformToDoc($src);
$result->formatOutput=true;
echo $result->savexml();


function doc($srcpath) {
    $doc = new DOMDocument;
    $doc->load($srcpath);
    return $doc;
}

的解释

xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"

使用多个文档的能力不是xslt 1.0规范的一部分。但是libxslt通过exsl支持它 - 所以我们“导入”它。

<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes"/>

只是让输出更漂亮的东西。与$result->formatOutput=true;一起使用。但是,如果你应该对空白有问题......看看这三行。

<xsl:param name="mergedoc" select="document($mergesrc)" />

这里第二个文档是“加载并存储在$ mergedoc中” $ mergesrc本身是一个通过$processor->setParameter('', 'mergesrc', '2.xml');

设置的参数
<xsl:template match="@* | node()">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
</xsl:template>

基本的xsl copy-all模板。没有更具体模板的每个节点都会深度复制到结果中。

现在,对于有趣的部分,匹配record元素的模板。

<xsl:variable name="idmerge" select="id" />

将当前记录元素的id元素的值存储在$ idmerge中。

<xsl:copy>
    <xsl:apply-templates />

我们希望将内容复制到结果中,并从当前记录元素的内容开始(xsl:apply-templates不带select属性,而copy-all模板就是这样做的。)

<xsl:apply-templates select="$mergedoc/records/record[id=$idmerge]/*[not(name()='id')]" />
来自mergedoc(2.xml)的

选择与{1.xml中的当前record元素具有相同id的record元素 - 存储在$ idmerge中。
在此记录中选择所有子节点 - 但不是id元素(已由1.xml的当前记录元素提供) - 并将其发送到copy-all模板(因为没有更多特定模板)。 / p>