XML :: LibXML和名称空间
我熟悉XML,并将其用于一些简单的用途。但是,我被要求创建一个自定义解决方案(必须使用Perl)来将付款提交给AuthorizeNet的网关。 AuthNet不支持Perl,所以我是独立的。XML :: LibXML和名称空间
我使用AuthNet的latest method,它依靠以XML格式发送和接收交易数据。
我设法使用Perl模块XML::LibXML成功提交和批准测试(沙箱)事务。
我试图解析返回数据时遇到了一个障碍。我终于明白,我是第一次处理命名空间。我得到的沙箱返回数据如下。
<createTransactionResponse
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
<refId />
<messages>
<resultCode>Ok</resultCode>
<message>
<code>I00001</code>
<text>Successful.</text>
</message>
</messages>
<transactionResponse>
<responseCode>1</responseCode>
<authCode>GTR92Q</authCode>
<avsResultCode>Y</avsResultCode>
<cvvResultCode>P</cvvResultCode>
<cavvResultCode>2</cavvResultCode>
<transId>40003699670</transId>
<refTransID />
<transHash>7E1148E18D5D0E0BE202EB1BCE4B0F09</transHash>
<testRequest>0</testRequest>
<accountNumber>XXXX1111</accountNumber>
<accountType>Visa</accountType>
<messages>
<message>
<code>1</code>
<description>This transaction has been approved.</description>
</message>
</messages>
<userFields>
<userField>
<name>MerchantDefinedFieldName1</name>
<value>MerchantDefinedFieldValue1</value>
</userField>
</userFields>
<transHashSha2 />
</transactionResponse>
</createTransactionResponse>
我需要从中提取某些值。我走,从AuthNet网关返回上述数据,将其存储在var $cont
,并准备:
my $dom = XML::LibXML->load_xml(string => $cont);
然后,我试图通过数据节点解析,搜索特定的值,例如:
foreach my $transres ($dom->findnodes("/createTransactionResponse/messages/message")) {
print "Message: " . $transres->to_literal() . "<br />\n";
}
这从不返回任何值。然后我意识到我正在处理命名空间问题。我试图注册的命名空间的许多变化,但无济于事,例如:
my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs("xsi", "http://www.w3.org/2001/XMLSchema-instance");
$xpc->registerNs("xsd", "http://www.w3.org/2001/XMLSchema");
foreach my $transres ($xpc->findnodes("//xsi:resultCode")) {
print "Result: " . $transres->to_literal() . "<br />\n";
}
这一切只是铸造一下,希望能对某种结果。我注意到,尽管返回数据引用了多个名称空间,但其余数据中的任何一个实际上都没有使用它们。我在XML命名空间问题上发现了一些*响应,但是我永远无法找到如何在XML数据中的其他地方未使用指定名称空间时引用节点的示例。比如,什么是默认的(如果你会的话)命名空间引用来使用,当没有命名空间被引用时 ......如果这是有道理的。
我遇到了一个entry elsewhere in *,其中它提到使用允许忽略名称空间的“本地名称”函数。我认为这可能会绕过这个问题,当我尝试它时,我终于看到了一些数据显示。
foreach my $transres ($dom->findnodes("//*[local-name()='resultCode']")) {
print "Result: " . $transres->to_literal() . "<br />\n";
}
这导致所需/期望值“Ok”的返回。它也适用于迄今我尝试过的其他节点。但是,我通常不愿意使用“只是忽略它”类型的解决方案,除非它们是真正合适的并且需要。
我的问题归结为这个问题:我使用的bypass-namespace方法是否最终从返回的XML数据中获取数据是可取的?
我想知道为什么AuthNet在返回数据中指定了多个名称空间,然后在数据中不使用它们 - 至少是我当前回收的数据。如果我绕过代码中的命名空间,但后来又如何,特别是当我们开始运行时,不同的命名空间会变得相关?我其实并不知道,AuthNet所谓的“支持”我已经能够达到的范围从无益到完全没有反应。
任何有关Perl/XML /名称空间问题经验丰富的建议/指导将不胜感激。
您是在广义上是正确的路线。问题是,你的XML数据使用它适用于那些没有明确的命名空间前缀
xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd"
当你观察到的,xsi
和xsd
前缀为您的数据未使用的所有标识默认命名空间,所以没有必要在你的Perl代码中指定它们。同时,XPath没有隐式名称空间的概念,因此您必须为AuthNet名称空间指定明确的缩写并将其用于所有XPath表达式中。
使用的缩写不必与XML数据中定义的缩写相同,并且在默认名称空间的情况下显然不能匹配。
下面是我将如何编码您访问resultCode
元素的方法。我总是喜欢用一个完整的XPath表达式从文档根目录,而不是搜索所有与元素,比方说在文件中,//anet:resultCode
my $dom = XML::LibXML->load_xml(string => $cont);
my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs(anet => 'AnetApi/xml/v1/schema/AnetApiSchema.xsd');
for my $transres ($xpc->findnodes('/anet:createTransactionResponse/anet:messages/anet:resultCode')) {
printf "Result: %s<br />\n", $transres->to_literal;
}
<ele xmlns="..."><foo/><bar/></ele>
是短期的
<p:ele xmlns:p="..."><p:foo/><p:bar/></p:ele>
所以要与命名空间AnetApi/xml/v1/schema/AnetApiSchema.xsd
和名称resultCode
元素。
my $xpc = XML::LibXML::XPathContext->new($dom);
$xpc->registerNs("a", "AnetApi/xml/v1/schema/AnetApiSchema.xsd");
for my $resultCode_node (
$xpc->findnodes("/a:createTransactionResponse/a:messages/a:resultCode")
) {
print("resultCode: " . text_to_html($resultCode_node->textContent()) . "<br />\n");
}
既然你注射文成HTML,你需要像下面这样:
my %escapes = (
"&" => "&",
"<" => "<",
">" => ">",
'"' => """,
"'" => "'",
);
my $class = join '', map quotemeta, keys(%escapes);
my $re = qr/([$class])/;
sub text_to_html { shift =~ s/$re/$escapes{$1}/rg }
是的! @Borodin,那些是我正在寻找的提示。您在命名空间问题上的说明有所不同,并帮助我解决了这个问题。我现在有一些工作代码可以成功解析AuthNet返回数据。 – user2226161