XML::LibXML 无法根据通过 HTTPS 提供的 DTD 进行验证
XML::LibXML cannot validate against DTD available over HTTPS
如何验证 XML 文档使用
XML::LibXML
何时可以通过 HTTPS 使用 DTD?
测试代码:
#!/usr/bin/perl -w
use XML::LibXML;
use strict;
my $xml = XML::LibXML->load_xml(IO => \*DATA);
my $dtd = XML::LibXML::Dtd->new( "-//NLM//DTD LinkOut 1.0//EN", "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" );
my $https_is_valid = $xml->is_valid( $dtd );
print "HTTPS dtd: ", ref $dtd, "\n Is valid: $https_is_valid\n";
my $dtd_http = XML::LibXML::Dtd->new( "-//NLM//DTD LinkOut 1.0//EN", "http://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" );
my $http_is_valid = $xml->is_valid( $dtd_http );
print "HTTP dtd: ", ref $dtd_http, "\n Is valid: $http_is_valid\n";
__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE LinkSet PUBLIC "-//NLM//DTD LinkOut 1.0//EN" "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" [
<!ENTITY base.url "https://some.domain.com">
<!ENTITY icon.url "https://some.domain.com/logo.png">
]>
<LinkSet>
<Link>
<LinkId>1</LinkId>
<ProviderId>XXXX</ProviderId>
<IconUrl>&icon.url;</IconUrl>
<ObjectSelector>
<Database>PubMed</Database>
<ObjectList>
<ObjId>1234567890</ObjId>
</ObjectList>
</ObjectSelector>
<ObjectUrl>
<Base>&base.url;</Base>
<Rule>/1/</Rule>
</ObjectUrl>
</Link>
</LinkSet>
上面的代码产生以下输出:
HTTPS dtd:
Is valid: 0
HTTP dtd: XML::LibXML::Dtd
Is valid: 1
DTD 无法从 HTTPS URL 加载,因此无法用于验证 XML。
我已经通过 HTTPS 下载了 DTD 并检查了 HTTP 重定向 - 没有。
我也看过
XML::LibXML::InputCallback
但看不出如何将它与 XML::LibXML::Dtd->new( ... );
.
合并
我应该如何实施此验证?
DTD 可通过 HTTP 获得,因此我可以使用它来验证,但这感觉就像我在避免问题而不是正确解决问题![=35=]
请注意,XML 已经包含 DTD 的 URL,因此您不需要创建 XML::LibXML::Dtd
来传递给 ->is_valid
。
我同意评论者 Grant McLean 的观点,您可能不想一直上网。事实上,不久前我写了一些代码,使用 XML::LibXML::InputCallback
将所有网络请求重定向到我缓存网络资源的本地 FS。
但要回答您的问题,通过安装 HTTP::Tiny
, which needs IO::Socket::SSL
>=1.56 and Net::SSLeay
>=1.49 以支持 SSL 来调整该代码以从网络(包括 HTTPS)获取数据并不难。以下打印预期的“Is valid: yes
”:
use warnings;
use strict;
use XML::LibXML;
use HTTP::Tiny;
use URI;
my $parser = XML::LibXML->new;
my $cb = XML::LibXML::InputCallback->new;
my $http = HTTP::Tiny->new;
my %cache;
$cb->register_callbacks([
sub { 1 }, # match (URI), returns Bool
sub { # open (URI), returns Handle
my $uri = URI->new($_[0]);
my $file;
#warn "Handling <<$uri>>\n"; #Debug
if (!$uri->scheme) { $file = $_[0] }
elsif ($uri->scheme eq 'file') { $file = $uri->path }
elsif ($uri->scheme=~/\Ahttps?\z/i) {
if (!defined $cache{$uri}) {
my $resp = $http->get($uri);
die "$uri: $resp->{status} $resp->{reason}\n"
unless $resp->{success};
$cache{$uri} = $resp->{content};
}
$file = $cache{$uri};
}
else { die "unsupported URL scheme: ".$uri->scheme }
open my $fh, '<', $file or die "$file: $!";
return $fh;
},
sub { # read (Handle,Length), returns Data
my ($fh,$len) = @_;
read($fh, my $buf, $len);
return $buf;
},
sub { close shift } # close (Handle)
]);
$parser->input_callbacks($cb);
my $doc = $parser->load_xml( IO => \*DATA );
print "Is valid: ", $doc->is_valid ? "yes" : "no", "\n";
__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE LinkSet PUBLIC "-//NLM//DTD LinkOut 1.0//EN" "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" [
<!ENTITY base.url "https://some.domain.com">
<!ENTITY icon.url "https://some.domain.com/logo.png">
]>
<LinkSet>
<Link>
<LinkId>1</LinkId>
<ProviderId>XXXX</ProviderId>
<IconUrl>&icon.url;</IconUrl>
<ObjectSelector>
<Database>PubMed</Database>
<ObjectList>
<ObjId>1234567890</ObjId>
</ObjectList>
</ObjectSelector>
<ObjectUrl>
<Base>&base.url;</Base>
<Rule>/1/</Rule>
</ObjectUrl>
</Link>
</LinkSet>
如何验证 XML 文档使用
XML::LibXML
何时可以通过 HTTPS 使用 DTD?
测试代码:
#!/usr/bin/perl -w
use XML::LibXML;
use strict;
my $xml = XML::LibXML->load_xml(IO => \*DATA);
my $dtd = XML::LibXML::Dtd->new( "-//NLM//DTD LinkOut 1.0//EN", "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" );
my $https_is_valid = $xml->is_valid( $dtd );
print "HTTPS dtd: ", ref $dtd, "\n Is valid: $https_is_valid\n";
my $dtd_http = XML::LibXML::Dtd->new( "-//NLM//DTD LinkOut 1.0//EN", "http://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" );
my $http_is_valid = $xml->is_valid( $dtd_http );
print "HTTP dtd: ", ref $dtd_http, "\n Is valid: $http_is_valid\n";
__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE LinkSet PUBLIC "-//NLM//DTD LinkOut 1.0//EN" "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" [
<!ENTITY base.url "https://some.domain.com">
<!ENTITY icon.url "https://some.domain.com/logo.png">
]>
<LinkSet>
<Link>
<LinkId>1</LinkId>
<ProviderId>XXXX</ProviderId>
<IconUrl>&icon.url;</IconUrl>
<ObjectSelector>
<Database>PubMed</Database>
<ObjectList>
<ObjId>1234567890</ObjId>
</ObjectList>
</ObjectSelector>
<ObjectUrl>
<Base>&base.url;</Base>
<Rule>/1/</Rule>
</ObjectUrl>
</Link>
</LinkSet>
上面的代码产生以下输出:
HTTPS dtd:
Is valid: 0
HTTP dtd: XML::LibXML::Dtd
Is valid: 1
DTD 无法从 HTTPS URL 加载,因此无法用于验证 XML。
我已经通过 HTTPS 下载了 DTD 并检查了 HTTP 重定向 - 没有。
我也看过
XML::LibXML::InputCallback
但看不出如何将它与 XML::LibXML::Dtd->new( ... );
.
我应该如何实施此验证?
DTD 可通过 HTTP 获得,因此我可以使用它来验证,但这感觉就像我在避免问题而不是正确解决问题![=35=]
请注意,XML 已经包含 DTD 的 URL,因此您不需要创建 XML::LibXML::Dtd
来传递给 ->is_valid
。
我同意评论者 Grant McLean 的观点,您可能不想一直上网。事实上,不久前我写了一些代码,使用 XML::LibXML::InputCallback
将所有网络请求重定向到我缓存网络资源的本地 FS。
但要回答您的问题,通过安装 HTTP::Tiny
, which needs IO::Socket::SSL
>=1.56 and Net::SSLeay
>=1.49 以支持 SSL 来调整该代码以从网络(包括 HTTPS)获取数据并不难。以下打印预期的“Is valid: yes
”:
use warnings;
use strict;
use XML::LibXML;
use HTTP::Tiny;
use URI;
my $parser = XML::LibXML->new;
my $cb = XML::LibXML::InputCallback->new;
my $http = HTTP::Tiny->new;
my %cache;
$cb->register_callbacks([
sub { 1 }, # match (URI), returns Bool
sub { # open (URI), returns Handle
my $uri = URI->new($_[0]);
my $file;
#warn "Handling <<$uri>>\n"; #Debug
if (!$uri->scheme) { $file = $_[0] }
elsif ($uri->scheme eq 'file') { $file = $uri->path }
elsif ($uri->scheme=~/\Ahttps?\z/i) {
if (!defined $cache{$uri}) {
my $resp = $http->get($uri);
die "$uri: $resp->{status} $resp->{reason}\n"
unless $resp->{success};
$cache{$uri} = $resp->{content};
}
$file = $cache{$uri};
}
else { die "unsupported URL scheme: ".$uri->scheme }
open my $fh, '<', $file or die "$file: $!";
return $fh;
},
sub { # read (Handle,Length), returns Data
my ($fh,$len) = @_;
read($fh, my $buf, $len);
return $buf;
},
sub { close shift } # close (Handle)
]);
$parser->input_callbacks($cb);
my $doc = $parser->load_xml( IO => \*DATA );
print "Is valid: ", $doc->is_valid ? "yes" : "no", "\n";
__DATA__
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE LinkSet PUBLIC "-//NLM//DTD LinkOut 1.0//EN" "https://www.ncbi.nlm.nih.gov/projects/linkout/doc/LinkOut.dtd" [
<!ENTITY base.url "https://some.domain.com">
<!ENTITY icon.url "https://some.domain.com/logo.png">
]>
<LinkSet>
<Link>
<LinkId>1</LinkId>
<ProviderId>XXXX</ProviderId>
<IconUrl>&icon.url;</IconUrl>
<ObjectSelector>
<Database>PubMed</Database>
<ObjectList>
<ObjId>1234567890</ObjId>
</ObjectList>
</ObjectSelector>
<ObjectUrl>
<Base>&base.url;</Base>
<Rule>/1/</Rule>
</ObjectUrl>
</Link>
</LinkSet>