使 XML::Simple 读取外部 DTD

Make XML::Simple read external DTDs

给定以下两个文件:

doc.xml

<!DOCTYPE TEST [

    <!ENTITY % get_em SYSTEM "entities.ent" >
    %get_em;

]>

<TEST>
        <COMPANY_ID>&COMPANY_ID;</COMPANY_ID>
</TEST>

entities.ent

<!ENTITY COMPANY_ID "84500">
<!ENTITY SPN_FIRM_ID "5900">
<!ENTITY SPN_CUSTD_REL_ID "40001">
<!ENTITY CUSTD_FIRM_NBR "229">
<!ENTITY CUSTD_FIRM_ID "5901">
<!ENTITY MASTERACCOUNT "TAL">

我可以成功使用xmllint:

xmllint --loaddtd --noent --dropdtd doc.xml
<?xml version="1.0"?>
<TEST>
        <COMPANY_ID>84500</COMPANY_ID>
</TEST>

我怎样才能让这个想法在 Perl 和 XML::Simple 中发挥作用?

$ perl -MData::Dumper -MXML::Simple -e 'print Dumper XMLin q{doc.xml}'
doc.xml:4: parser error : PEReference: %get_em; not found
    %get_em;
            ^
doc.xml:9: parser error : Entity 'COMPANY_ID' not defined
        <COMPANY_ID>&COMPANY_ID;</COMPANY_ID>
                                ^

经过一些评论,我试过 XML::LibXML::Simple 它看起来确实好一点,但实体仍然没有得到解决

$ perl -MData::Dumper -MXML::LibXML::Simple -e 'print Dumper XMLin q{doc.xml}'
./doc.xml:9: parser error : Entity 'COMPANY_ID' not defined
        <COMPANY_ID>&COMPANY_ID;</COMPANY_ID>
                                ^

嗯,上面的 PEReference 很突出.. PE 是什么? 但更重要的是,如何让 Perl 使用 XML::Simple 读取外部 DTD?

我厌倦了 XML::Simple::DTDReader 但我发现这个模块非常有限制,尤其是它指出 specifically XML::Simple 的无数选项中的 none 是支持!

如果我在 doc.xml 本身中包含 ENTITY 声明,它确实有效.. 显然 XML::Simple 知道 如何处理 DOCTYPE 只有我想将外部 DTD 与 SYSTEM 一起使用,这就是我要让它工作的地方。

我仍在寻找这是否可以在 Perl 本身中完成,但一个简单的方法是将我发现的内容与 xmllint and pass as file handle to XMLin!

结合起来
$ perl -MData::Dumper -MXML::Simple -e 'open my $fh, "xmllint --loaddtd --noent --dropdtd doc.xml |"; print Dumper XMLin $fh'
$VAR1 = {
          'COMPANY_ID' => '84500'
        };

XML::LibXML默认会展开实体,所以可以使用

$ perl -e'
    use Data::Dumper qw( Dumper );
    use XML::LibXML  qw( );
    use XML::Simple  qw( XMLin );

    my $xml = XML::LibXML->new()->parse_file("doc.xml")->toString();
    my $doc = XMLin($xml);
    print(Dumper($doc));
'
$VAR1 = {
          'COMPANY_ID' => '84500'
        };

这也可以通过 XML::LibXML::Simple 覆盖 XML::Simple 兼容性设置来实现。

$ perl -e'
    use Data::Dumper        qw( Dumper );
    use XML::LibXML::Simple qw( XMLin );

    my $doc = XMLin("doc.xml",
        ParserOpts => {
            load_ext_dtd    => 1,
            ext_ent_handler => undef,
        },
    );
    print(Dumper($doc));
'
$VAR1 = {
          'COMPANY_ID' => '84500'
        };