Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何比较不同XML文档中的某些值?_Xml_Perl_Xml Parsing_Perl Module - Fatal编程技术网

如何比较不同XML文档中的某些值?

如何比较不同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中 你能给我一些建议吗?我应该使用什么模块?我应

我想用Perl编写代码来比较两个XML文件

历史上的一点点。。。 通过API文档(get请求),我从Web服务1获取数据1,从服务2获取数据2。它们以XML格式表示,但不同

我应该只比较这些文件中的两个元素(deviceName和ipAddress),如果两个文件中的元素相同,则应该是消息“WebService1已包含deviceName“Switch1”。如果没有-我会发出POST请求并将此设备添加到WebService1/WebService2中

你能给我一些建议吗?我应该使用什么模块?我应该如何开始比较

例如(文件1)


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;
    }