如何比较不同XML文档中的某些值?
我想用Perl编写代码来比较两个XML文件 历史上的一点点。。。 通过API文档(get请求),我从Web服务1获取数据1,从服务2获取数据2。它们以XML格式表示,但不同 我应该只比较这些文件中的两个元素(deviceName和ipAddress),如果两个文件中的元素相同,则应该是消息“WebService1已包含deviceName“Switch1”。如果没有-我会发出POST请求并将此设备添加到WebService1/WebService2中 你能给我一些建议吗?我应该使用什么模块?我应该如何开始比较 例如(文件1)如何比较不同XML文档中的某些值?,xml,perl,xml-parsing,perl-module,Xml,Perl,Xml Parsing,Perl Module,我想用Perl编写代码来比较两个XML文件 历史上的一点点。。。 通过API文档(get请求),我从Web服务1获取数据1,从服务2获取数据2。它们以XML格式表示,但不同 我应该只比较这些文件中的两个元素(deviceName和ipAddress),如果两个文件中的元素相同,则应该是消息“WebService1已包含deviceName“Switch1”。如果没有-我会发出POST请求并将此设备添加到WebService1/WebService2中 你能给我一些建议吗?我应该使用什么模块?我应
0
0
205571
NEW-SW5
思科交换机
0
10.66.12.128
0
0
可达成的
国际标准化组织
12.1(22)
0
文件2
假的
ASCII码
半径
******
10.66.12.128
21
位置#所有位置
设备类型#所有设备类型
在文件1中有一个特殊的smth:my标记,名为:deviceName,ipAddress,它们是元素在文件2中,我们有一个属性(因为它位于主元素ns3:networkdevice中,名为name,它从文件1响应我们的设备名),另一个元素名为ipaddress(文件1中的ipaddress)这不是一个简单的从头开始编写的任务。你应该利用这不是一个简单的从头开始写的任务。您应该使用来解析这两个响应。它们中的每一个都需要一个单独的解析器 对于第一个,您需要选择两个标记
和
。对于访问匹配元素属性的每个元素,一个简单的方法就足够了
这些处理程序可能很复杂,但在我们的例子中,处理单个值的代码引用就足够了。我们知道每个值只出现一次,所以我们可以直接将它们分配给各自的词汇变量
use strict;
use warnings;
use XML::Twig;
my ($device_name, $ip_address);
XML::Twig->new(
twig_handlers => {
deviceName => sub { $device_name = $_->text },
ipAddress => sub { $ip_address = $_->text },
}
)->parse(\*DATA);
say $device_name;
say $ip_address;
__DATA__
<?xml version="1.0" ?>
<queryResponse last="34" first="0" count="35" type="Devices" responseType="listEntityInstances" requestUrl="https://hostname/webacs/api/v1/data/Devices?.full=true" rootUrl="https://hostname/webacs/api/v1/data">
<entity dtoType="devicesDTO" type="Devices" url="https://hostname/webacs/api/v1/data/Devices/201">
<devicesDTO displayName="201201" id="201">
<clearedAlarms>0</clearedAlarms>
<collectionDetail></collectionDetail>
<collectionTime></collectionTime>
<creationTime></creationTime>
<criticalAlarms>0</criticalAlarms>
<deviceId>205571</deviceId>
<deviceName>NEW-SW5</deviceName>
<deviceType>Cisco Switch</deviceType>
<informationAlarms>0</informationAlarms>
<ipAddress>10.66.12.128</ipAddress>
<location></location>
<majorAlarms>0</majorAlarms>
<managementStatus></managementStatus>
<manufacturerPartNrs>
<manufacturerPartNr></manufacturerPartNr>
</manufacturerPartNrs>
<minorAlarms>0</minorAlarms>
<productFamily></productFamily>
<reachability>Reachable</reachability>
<softwareType>IOS</softwareType>
<softwareVersion>12.1(22)</softwareVersion>
<warningAlarms>0</warningAlarms>
</devicesDTO>
</entity>
</queryResponse>
现在你有了这两个,你可以把它们结合起来。我建议为它们中的每一个创建一个函数,传入响应XML,让它们返回$device\u name
和$ip\u address
use strict;
use warnings;
use XML::Twig;
sub parse_response_1 {
my $xml = shift;
my ( $device_name, $ip_address );
XML::Twig->new(
twig_handlers => {
deviceName => sub { $device_name = $_->text },
ipAddress => sub { $ip_address = $_->text },
}
)->parse($xml);
return $device_name, $ip_address;
}
sub parse_response_2 {
my $xml = shift;
my ( $device_name, $ip_address );
XML::Twig->new(
twig_handlers => {
'ns3:networkdevice' => sub { $device_name = $_->att('name') },
ipaddress => sub { $ip_address = $_->text },
}
)->parse($xml);
return $device_name, $ip_address;
}
当然,我的名字parse_response_1
和parse_response_2
不是最好的选择。不要使用数字,而是使用返回响应的服务的名称
通过这两个函数,我们现在可以准确地检索所需的信息。剩下的就是检查它们了
sub check {
my ( $response_1, $response_2 ) = @_;
my ( $device_name_1, $ip_address_1 ) = parse_response_1($response_1);
my ( $device_name_2, $ip_address_2 ) = parse_response_2($response_2);
return $device_name_1 eq $device_name_2 && $ip_address_1 eq $ip_address_2;
}
同样,变量的名称可能更好。现在您只需要用两个响应XML调用它,它将返回一个真实值,或者不返回。您可以使用它来解析这两个响应。它们中的每一个都需要一个单独的解析器
use XML::Simple;
use Data::Dumper;
my $file1_ref = XMLin("./file1");
my $file2_ref = XMLin("./file2");
if($file2_ref->{NetworkDeviceIPList}->{NetworkDeviceIP}->{ipaddress} eq $file1_ref->{entity}->{devicesDTO}->{ipAddress} && $file2_ref->{name} eq $file1_ref->{entity}->{devicesDTO}->{deviceName}) {
print "WebService1 already contains DeviceName \"".$file2_ref->{name}."\"\n";
} else {
# POST request and add this device in WebService1/WebService2
# Code here ....
}
对于第一个,您需要选择两个标记
和
。对于访问匹配元素属性的每个元素,一个简单的方法就足够了
这些处理程序可能很复杂,但在我们的例子中,处理单个值的代码引用就足够了。我们知道每个值只出现一次,所以我们可以直接将它们分配给各自的词汇变量
use strict;
use warnings;
use XML::Twig;
my ($device_name, $ip_address);
XML::Twig->new(
twig_handlers => {
deviceName => sub { $device_name = $_->text },
ipAddress => sub { $ip_address = $_->text },
}
)->parse(\*DATA);
say $device_name;
say $ip_address;
__DATA__
<?xml version="1.0" ?>
<queryResponse last="34" first="0" count="35" type="Devices" responseType="listEntityInstances" requestUrl="https://hostname/webacs/api/v1/data/Devices?.full=true" rootUrl="https://hostname/webacs/api/v1/data">
<entity dtoType="devicesDTO" type="Devices" url="https://hostname/webacs/api/v1/data/Devices/201">
<devicesDTO displayName="201201" id="201">
<clearedAlarms>0</clearedAlarms>
<collectionDetail></collectionDetail>
<collectionTime></collectionTime>
<creationTime></creationTime>
<criticalAlarms>0</criticalAlarms>
<deviceId>205571</deviceId>
<deviceName>NEW-SW5</deviceName>
<deviceType>Cisco Switch</deviceType>
<informationAlarms>0</informationAlarms>
<ipAddress>10.66.12.128</ipAddress>
<location></location>
<majorAlarms>0</majorAlarms>
<managementStatus></managementStatus>
<manufacturerPartNrs>
<manufacturerPartNr></manufacturerPartNr>
</manufacturerPartNrs>
<minorAlarms>0</minorAlarms>
<productFamily></productFamily>
<reachability>Reachable</reachability>
<softwareType>IOS</softwareType>
<softwareVersion>12.1(22)</softwareVersion>
<warningAlarms>0</warningAlarms>
</devicesDTO>
</entity>
</queryResponse>
现在你有了这两个,你可以把它们结合起来。我建议为它们中的每一个创建一个函数,传入响应XML,让它们返回$device\u name
和$ip\u address
use strict;
use warnings;
use XML::Twig;
sub parse_response_1 {
my $xml = shift;
my ( $device_name, $ip_address );
XML::Twig->new(
twig_handlers => {
deviceName => sub { $device_name = $_->text },
ipAddress => sub { $ip_address = $_->text },
}
)->parse($xml);
return $device_name, $ip_address;
}
sub parse_response_2 {
my $xml = shift;
my ( $device_name, $ip_address );
XML::Twig->new(
twig_handlers => {
'ns3:networkdevice' => sub { $device_name = $_->att('name') },
ipaddress => sub { $ip_address = $_->text },
}
)->parse($xml);
return $device_name, $ip_address;
}
当然,我的名字parse_response_1
和parse_response_2
不是最好的选择。不要使用数字,而是使用返回响应的服务的名称
通过这两个函数,我们现在可以准确地检索所需的信息。剩下的就是检查它们了
sub check {
my ( $response_1, $response_2 ) = @_;
my ( $device_name_1, $ip_address_1 ) = parse_response_1($response_1);
my ( $device_name_2, $ip_address_2 ) = parse_response_2($response_2);
return $device_name_1 eq $device_name_2 && $ip_address_1 eq $ip_address_2;
}
同样,变量的名称可能更好。现在您只需要用两个响应XML调用它,它将返回一个真实值,或者不返回
use XML::Simple;
use Data::Dumper;
my $file1_ref = XMLin("./file1");
my $file2_ref = XMLin("./file2");
if($file2_ref->{NetworkDeviceIPList}->{NetworkDeviceIP}->{ipaddress} eq $file1_ref->{entity}->{devicesDTO}->{ipAddress} && $file2_ref->{name} eq $file1_ref->{entity}->{devicesDTO}->{deviceName}) {
print "WebService1 already contains DeviceName \"".$file2_ref->{name}."\"\n";
} else {
# POST request and add this device in WebService1/WebService2
# Code here ....
}
您可以将调用转换为方法,我强烈建议您添加和评估转换,并检查错误,以防返回的XML有问题
您可以将调用转换为方法,我强烈建议您在转换过程中添加和评估,并检查错误,以防返回的XML有问题。首先请注意,对于两个XML文件“相同”的含义,目前还没有一致的意见。例如,每个人都同意应该忽略开始标记和结束标记中的空格,属性周围的单引号和双引号之间的区别是不相关的,属性可以是任意顺序的;但在如何处理注释、元素标记之间的空白、名称空间前缀和许多其他细节方面,需求各不相同 要求不同的另一个方面是,当文档被视为不同时,您需要什么信息。有些机制只会给你一个肯定或否定的答案,并不会帮助你找到差异 这样做的结果是,可能会有通用的解决方案,但它们可能并不总是满足您的特定需求 因此,如果您准备编写几百行代码,那么编写自己的比较器并不是一个荒谬的想法
但是,如果你能找到在Perl环境中运行的例子,你可以考虑的两个现成的解决方案是:
- XML规范化器:规范化两个文档,然后在二进制级别比较结果
#!/usr/bin/env perl use strict; use warnings; use XML::LibXML; my %compare = ( '//deviceName' => '//@name', '//ipAddress' => '//ipaddress' ); my $search1 = XML::LibXML::XPathContext->new( XML::LibXML->load_xml( location => 'test1a.xml' ) ); my $search2 = XML::LibXML::XPathContext->new( XML::LibXML->load_xml( location => 'test1b.xml' ) ); foreach my $key ( keys %compare ) { my $first = $search1->find($key); my $second = $search2->find( $compare{$key} ); print "$key = $first\n"; print "$compare{$key} = $second\n"; print "Matches found\n" if $first eq $second; }