如何使用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();
答案 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>