Perl使用XML::LibXML如何生成不带尾部自闭合斜杠的XML标签
问题根因
你遇到的根标签自闭合不是API调用错误,是XML::LibXML符合XML标准的默认行为:当元素没有任何子节点(包括子元素、文本节点、注释等所有子内容)时,库会自动输出短格式的自闭合标签。在XML规范中,<spirit:component/>和<spirit:component></spirit:component>语义完全等价,所有合规XML解析器都会将二者识别为相同内容,不会影响文档合法性和后续解析。
你当前测试输出时根节点还未添加任何子内容,所以才会生成自闭合形式,等你后续把业务需要的所有子元素、子节点追加到根节点下再输出,自然就不会出现自闭合标签。
强制输出非自闭合标签的方案
如果你的下游处理程序不兼容标准自闭合标签(比如用正则匹配XML的非标准实现),可以选择以下两种方案:
- 方案1:给根节点追加空文本节点(最简单,无额外依赖)
只要元素存在子内容,库就不会输出自闭合标签,在设置完根节点后追加一行代码即可:
该操作不会增加文档可见内容,输出时会得到$root->appendTextNode('');<spirit:component ...></spirit:component>的成对标签形式。 - 方案2:输出时关闭空标签短格式选项
引入XML::LibXML对应常量,在调用toString时开启XML_LIBXML_NO_EMPTYTAGS选项,强制所有空元素输出成对标签:use XML::LibXML qw(XML_LIBXML_NO_EMPTYTAGS); print $doc->toString(1 | XML_LIBXML_NO_EMPTYTAGS);
额外编码建议
你当前手动通过setAttribute设置xmlns:*命名空间属性的方式不是最佳实践,容易出现后续命名空间前缀错乱、属性重复声明的问题,推荐使用库提供的原生命名空间API处理,参考实现如下:
use strict; use warnings; use XML::LibXML; my $doc = XML::LibXML::Document->new('1.0', 'utf-8'); # 定义各命名空间URI my $ns_spirit = "http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.4"; my $ns_vendor_ext = "$ENV{IREG_GEN}/XMLSchema/SPIRIT"; my $ns_xsi = "http://www.w3.org/2001/XMLSchema-instance"; # 创建带spirit命名空间的根节点 my $root = $doc->createElementNS($ns_spirit, 'spirit:component'); # 声明其余命名空间前缀 $root->setNamespace($ns_vendor_ext, 'vendorExtensions', 0); $root->setNamespace($ns_xsi, 'xsi', 0); # 设置schemaLocation属性,属性值不要保留换行、制表符,避免被转义为字符实体 $root->setAttributeNS( $ns_xsi, 'xsi:schemaLocation', "$ENV{IREG_GEN}/XMLSchema/SPIRIT/VendorExtensions.xsd http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.4 http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.4/index.xsd" ); $doc->setDocumentElement($root); # 后续往$root下追加所有业务子节点后再输出,即可得到符合预期的非自闭合标签 print $doc->toString(1);
注:如果需要保留
$IREG_GEN字面量而不是读取环境变量替换,把代码里的$ENV{IREG_GEN}换成'$IREG_GEN'即可。
内容的提问来源于stack exchange,提问作者Monica
相关产品推荐
相关产品推荐

