使用perl LibXML Element-> getAttribute()而不在值中扩展unicode实体

时间:2019-02-27 18:46:50

标签: xml perl unicode xml-libxml xml-entities

我目前正在尝试创建一个使用LibXML来处理SVG字体数据的perl脚本。

在SVG字体中,每个字符都被定义为带有unicode属性的字形元素,该属性以unicode实体的形式定义其unicode地址;像这样:

<glyph unicode="&#x2000;" />

我想要做的一部分就是获取每个字形元素的unicode属性的值,并将其像字符串一样处理。但是,当我使用Element-> getAttribute('unicode');时,在字形节点上,它返回一个“宽字符”,显示为占位符矩形,这使我相信它将Unicode实体扩展为Unicode字符并返回该字符。

当我创建解析器时,我将expand_entities设置为0,所以我不确定还能做些什么来防止这种情况。我对XML处理非常陌生,所以我不确定我是否真正了解正在发生的事情,或者甚至认为这是可以预防的。

这是一个代码示例:

use utf8;
use open ':std', ':encoding(UTF-8)';
use strict;
use warnings;
use XML::LibXML;
$XML::LibXML::skipXMLDeclaration = 1;

my $xmlFile = $ARGV[0];

my $parser = XML::LibXML->new();
$parser->load_ext_dtd(0);
$parser->validation(0);
$parser->no_network(1);
$parser->recover(1);
$parser->expand_entities(0);

my $xmlDom = $parser->load_xml(location => $xmlFile);

my $xmlDomSvg = XML::LibXML::XPathContext->new();
$xmlDomSvg->registerNs('svg', 'http://www.w3.org/2000/svg');

foreach my $myGlyph ($xmlDomSvg->findnodes('/svg:svg/svg:defs/svg:font/svg:glyph', $xmlDom))
{
  my $myGlyphCode = $myGlyph->getAttribute('unicode');
  print $myGlyphCode . "\n";
}

注意:如果运行print $ myGlyph-> toString();,输出中的unicode实体不会扩展,因此为什么我得出结论说扩展发生在getAttribute方法中。

2 个答案:

答案 0 :(得分:2)

这可能不是您要寻找的答案,但是恕我直言getAttribute为您提供了足够的信息(例如,Perl字符串)以另一种方式解决您的问题。您正在尝试将该Perl字符串写入非UTF8文件,这就是为什么会收到“宽字符”警告的原因。

一个精简示例,说明如何获取您要寻找的U+xxxx值:

use strict;
use warnings;
use open qw(:encoding(UTF-8) :std);

use XML::LibXML;

my $dom = XML::LibXML->load_xml(IO => \*DATA)
    or die "XML\n";
my $root = $dom->documentElement();
print $root->toString(), "\n";

my $attr = $root->getAttribute('unicode');
printf("'%s' is %d (U+%04X)\n", $attr, ord($attr), ord($attr));

exit 0;

__DATA__
<glyph unicode="&#x2000;" />

试运行:

$ perl dummy.pl
<glyph unicode="&#x2000;"/>
' ' is 8192 (U+2000)

更新expand_entities的文档是恕我直言的误导。它讨论的是“实体”,但显然意味着ENTITY定义,即文档中引入的 new 实体。不幸的是,libxml2 documentation并不清晰。但是这个old message似乎表明您描述的行为是预期的,即。 XML解析器应始终替换预定义的实体:

#!/usr/bin/perl
use warnings;
use strict;

use XML::LibXML;

my $parser = XML::LibXML->new({
    expand_entities => $ARGV[0] ? 1 : 0,
});

my $dom = $parser->load_xml(IO => \*DATA)
    or die "XML\n";

my $root = $dom->documentElement();
print "toString():  ", $root->toString(), "\n";
print "textContent: ", $root->textContent(), "\n";

my $attr = $root->getAttribute('test');
print "attribute:   ${attr}\n";

exit 0;

__DATA__
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY author "Fluffy Bunny">
]>
<tag test="&lt;&author;&gt;">&lt;&author;&gt;</tag>

试运行:

$ perl dummy.pl 0
toString():  <tag test="&lt;&author;&gt;">&lt;&author;&gt;</tag>
textContent: <Fluffy Bunny>
attribute:   <Fluffy Bunny>

$ perl dummy.pl 1
toString():  <tag test="&lt;Fluffy Bunny&gt;">&lt;Fluffy Bunny&gt;</tag>
textContent: <Fluffy Bunny>
attribute:   <Fluffy Bunny>

答案 1 :(得分:1)

serializeContent()方法可能会满足您的要求:

my $xml = '<doc>
  <glyph unicode="&#x2000;" />
</doc>';

my $dom = XML::LibXML->load_xml(
    string          => $xml,
    expand_entities => 0,
    no_network      => 1,
);

my($attr) = $dom->findnodes('//glyph[1]/@unicode');

say $attr->serializeContent();

哪个输出:

&#x2000;

我怀疑expand_entities选项不适用于数字字符实体。该文档尚不清楚,并且我没有查看源代码。

在更常见的情况下,您要做要扩展所有实体,而只希望这些实体表示的实际字符,则甚至不需要调用getAttribute()。每个节点对象都使用tied hash接口,因此您可以执行以下操作:

my $text = $glyph->{unicode};