我正在使用 PHP 8.2,需要将 XML 节点从一个文档导入到另一个文档,而 PHP 不自动添加“默认”命名空间前缀。这破坏了 XML 中的数字签名。
我有以下 XML (
$xmlA
):
<A xmlns="">
<B xmlns="">
<C>Value</C>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignatureValue>...</SignatureValue>
</Signature>
</B>
</A>
我想将
<B>
节点导入到另一个 XML ($xmlX
):
<X xmlns="">
<Y>Value</Y>
</X>
当我使用
DOMDocument
和 appendChild
或 insertBefore
将 <B>
节点从 $xmlA
导入到 $xmlX
时,PHP 向 <Signature>
节点添加默认命名空间前缀,这是我不想要的:
$xmlA = <<<XML
<A xmlns="">
<B xmlns="">
<C>Value</C>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignatureValue>...</SignatureValue>
</Signature>
</B>
</A>
XML;
$xmlX = <<<XML
<X xmlns="">
<Y>Value</Y>
</X>
XML;
$domA = new DOMDocument();
$domA->loadXML($xmlA);
$domX = new DOMDocument();
$domX->loadXML($xmlX);
$nodeB = $domA->getElementsByTagName('B')->item(0);
$nodeB = $domX->importNode($nodeB, true);
var_dump($nodeB->getElementsByTagName('Signature')->item(0)->prefix); //string(0) ""
$domX->documentElement->appendChild($nodeB);
var_dump($nodeB->getElementsByTagName('Signature')->item(0)->prefix); //string(7) "default"
$domX->save('out.xml');
结果(按原样复制)是:
<?xml version="1.0"?>
<X xmlns="">
<Y>Value</Y>
<B xmlns="" xmlns:default="http://www.w3.org/2000/09/xmldsig#">
<C>Value</C>
<default:Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<default:SignatureValue>...</default:SignatureValue>
</default:Signature>
</B></X>
我希望生成的 XML 保持原始结构和命名空间,而不需要任何额外的前缀(不需要缩进):
<?xml version="1.0"?>
<X xmlns="">
<Y>Value</Y>
<B xmlns="">
<C>Value</C>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignatureValue>...</SignatureValue>
</Signature>
</B>
</X>
如何在 PHP 中将
<B>
节点从 $xmlA
导入到 $xmlX
,而不需要 PHP 自动添加命名空间前缀或更改 XML 结构?
我正在使用
DOMDocument
和 SimpleXML
,但 PHP 在导入 XML 节点时不断添加不需要的名称空间前缀,这在我的情况下破坏了数字签名。我浏览了 Stack Overflow 上的几个相关问题并尝试了各种方法,但都没有解决问题。
是否有一种可靠的方法可以在 PHP 中执行此操作,而无需将输出作为字符串进行操作以删除添加的前缀?或者,我知道我可以开始一致地使用前缀,但我需要导入的大多数子 XML 都没有它们,我需要维护这种格式。
后记:我必须将 A 和 B 的命名空间替换为空字符串,以便 StackOverflow 让我提交问题,但效果是一样的。
还有其他可用的 XML 堆栈,例如 SaxonC 12 HE(当前版本为 12.5)(https://www.saxonica.com/download/c.xml),您可以在其中使用 XSLT 3 例如
$xmlA = <<<XML
<A xmlns="">
<B xmlns="">
<C>Value</C>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignatureValue>...</SignatureValue>
</Signature>
</B>
</A>
XML;
$xmlX = <<<XML
<X xmlns="">
<Y>Value</Y>
</X>
XML;
$xsltCode1 = <<<XML
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:param name="doc2"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
<xsl:copy-of select="\$doc2/descendant::B[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
XML;
$saxonProc = new Saxon\SaxonProcessor();
$xsltProc = $saxonProc->newXslt30Processor();
$xsltExecutable = $xsltProc->compileFromString($xsltCode1);
$xsltExecutable->setParameter('{}doc2', $saxonProc->parseXmlFromString($xmlA));
$resultDoc = $xsltExecutable->transformToValue($saxonProc->parseXmlFromString($xmlX));
echo $resultDoc;
unset($xsltProc);
unset($xsltExecutable);
unset($resultDoc);
unset($saxonProc);
结果例如
<X>
<Y>Value</Y>
<B>
<C>Value</C>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignatureValue>...</SignatureValue>
</Signature>
</B>
</X>
请注意,多余的
xmlns=""
不会在 XML/XSLT 解析/处理/序列化中保留。