如何防止 PHP 在将子 XML 导入另一个 XML 时添加默认命名空间前缀

问题描述 投票:0回答:1

我正在使用 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 digital-signature domdocument php-8.2
1个回答
0
投票

还有其他可用的 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 解析/处理/序列化中保留。

© www.soinside.com 2019 - 2024. All rights reserved.