如何比较来自不同 XML 文档的某些值?
How do I compare certain values from different XML documents?
我想用 Perl 编写代码来比较两个 XML 文件。
一点点历史……
使用 API 文档(获取请求),我从 Web Service1 获取数据 1,从 Service2 获取数据 2。它们以 XML 格式显示,但不相同。
我应该比较这些文件中的两个元素(deviceName 和 ipAddress),如果它们在两个文件中相同,它应该是一条消息“WebService1 已经包含 DeviceName "Switch1"”。如果没有 - 我会提出 POST 请求并在 WebService1/WebService2.
中添加此设备
你能给我建议吗,我应该使用什么模块,我应该如何开始这个比较?
例如 (file1)
<?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>
文件 2
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns3:networkdevice name="NEW-SW5" id="9a6ef750-2620-11e4-81be-b83861d71f95" xmlns:ns2="ers.ise.cisco.com" xmlns:ns3="network.ers.ise.cisco.com">
<link type="application/xml" href="https://hostname:9060/ers/config/networkdevice/123456" rel="self"/>
<authenticationSettings>
<enableKeyWrap>false</enableKeyWrap>
<keyInputFormat>ASCII</keyInputFormat>
<networkProtocol>RADIUS</networkProtocol>
<radiusSharedSecret>******</radiusSharedSecret>
</authenticationSettings>
<NetworkDeviceIPList>
<NetworkDeviceIP>
<ipaddress>10.66.12.128</ipaddress>
<mask>21</mask>
</NetworkDeviceIP>
</NetworkDeviceIPList>
<NetworkDeviceGroupList>
<NetworkDeviceGroup>Location#All Locations</NetworkDeviceGroup>
<NetworkDeviceGroup>Device Type#All Device Types</NetworkDeviceGroup>
</NetworkDeviceGroupList>
</ns3:networkdevice>
有一些特别之处:在文件 1 中,我的标签名为:deviceName、ipAddress,它们是 elements.
在 file2 中,我们有一个属性(因为它位于主要元素 ns3:networkdevice 中,它被称为 name ,它从 file1 响应我们的 deviceName )和另一个元素称为 ipaddress(文件 1 中的 ipAddress)
这不是一项从头开始编写的简单任务。你应该使用 XML::Compare
您可以使用 XML::Twig 来解析这两个响应。他们每个人都需要一个单独的解析器。
对于第一个,您需要选择 <deviceName>
和 <ipAddress>
这两个标签。匹配元素的简单 twig_handler
for each of them that access the text
属性 就足够了。
这些处理程序可能很复杂,但在我们的例子中,处理单个值的代码参考就足够了。我们知道每个值只有一次出现,所以我们可以直接将它们都赋值给各自的词法变量。
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>
对于第二个,您需要使用 att()
来获取其中一个元素的 name 属性,但这也很简单。
use strict;
use warnings;
use XML::Twig;
my ($device_name, $ip_address);
XML::Twig->new(
twig_handlers => {
'ns3:networkdevice' => sub { $device_name = $_->att('name') },
ipaddress => sub { $ip_address = $_->text },
}
)->parse(\*DATA);
say $device_name;
say $ip_address;
__DATA__
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns3:networkdevice name="NEW-SW5" id="9a6ef750-2620-11e4-81be-b83861d71f95" xmlns:ns2="ers.ise.cisco.com" xmlns:ns3="network.ers.ise.cisco.com">
<link type="application/xml" href="https://hostname:9060/ers/config/networkdevice/123456" rel="self"/>
<authenticationSettings>
<enableKeyWrap>false</enableKeyWrap>
<keyInputFormat>ASCII</keyInputFormat>
<networkProtocol>RADIUS</networkProtocol>
<radiusSharedSecret>******</radiusSharedSecret>
</authenticationSettings>
<NetworkDeviceIPList>
<NetworkDeviceIP>
<ipaddress>10.66.12.128</ipaddress>
<mask>21</mask>
</NetworkDeviceIP>
</NetworkDeviceIPList>
<NetworkDeviceGroupList>
<NetworkDeviceGroup>Location#All Locations</NetworkDeviceGroup>
<NetworkDeviceGroup>Device Type#All Device Types</NetworkDeviceGroup>
</NetworkDeviceGroupList>
</ns3:networkdevice>
现在你已经拥有了这两个,你可以将它们结合起来。我建议为它们中的每一个创建一个函数,传入响应 XML 并使它们 return 成为 $device_name
和 $ip_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
不是最好的选择。不要使用数字,而是使用 return 编辑响应的服务名称。
有了这两个函数,我们现在可以准确地检索我们想要的信息。剩下的就是检查它们了。
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 调用它,它会 return 一个真值,或者不是。
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 文件是 "the same" 的含义,目前还没有普遍的共识。例如,每个人都同意忽略开始和结束标记中的空格,属性周围的单引号和双引号之间的区别无关紧要,属性可以按任何顺序排列;但对于如何处理注释、元素标签之间的空格、命名空间前缀和许多其他细节,要求各不相同。
另一个要求不同的领域是当文件被认为不同时您需要什么信息。有些机制只会给你一个是或否的答案,不会帮助你找到差异。
这会导致可能存在通用解决方案,但它们可能并不总能满足您的特定要求。
因此,如果您准备编写几百行代码,那么编写自己的比较器并不是一个荒谬的想法。
但是如果您能在 Perl 环境中找到 运行 的示例,您可以考虑两个现成的解决方案:
XML canonicalizers:规范化两个文档,然后在二进制级别比较结果。
XPath 2.0: 提供函数 deep-equal() 来比较两个节点(包括文档节点)
很像 simbaque I'd use XML::Twig
,尽管我处理它的方式略有不同 - 我提供这个是为了比较 - 而不是使用 twig_handlers
- 我称之为强大的和有用的技术,但特别适合增量解析更大的 XML - 使用 get_xpath
在 XML 中查找基于 xpath
的引用的东西可能提供替代方案。
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $xml1 = XML::Twig->new->parsefile('test1a.xml');
my $xml2 = XML::Twig->new->parsefile('test1b.xml');
if ( $xml1->get_xpath( '//deviceName', 0 )->text
eq $xml2->root->att('name') )
{
print "Name matches\n";
}
if ( $xml1->get_xpath( '//ipAddress', 0 )->text
eq $xml2->get_xpath( '//ipaddress', 0 )->text )
{
print "IP matches\n";
}
我们将两个文件解析成一个XML::Twig
对象,然后使用get_xpath
查找节点位置。 //
表示树中的任何地方, 0
指的是哪个实例(例如第一个,唯一)。
理想情况下,我们可能会做一些 xpath 字符串来直接比较 - 我们不能在这里,因为 'name' 属性是根节点的属性(也是 XML::Twig
xpath引擎是你不能直接select属性内容)。
但是 XML::LibXML
- 功能更全面,代价是学习曲线有点陡峭。我一般不会使用它 但在这种特定情况下,它可以将 xpath
表达式处理为 select 根节点的属性。
所以会是这样的:
#!/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;
}
我想用 Perl 编写代码来比较两个 XML 文件。
一点点历史…… 使用 API 文档(获取请求),我从 Web Service1 获取数据 1,从 Service2 获取数据 2。它们以 XML 格式显示,但不相同。
我应该比较这些文件中的两个元素(deviceName 和 ipAddress),如果它们在两个文件中相同,它应该是一条消息“WebService1 已经包含 DeviceName "Switch1"”。如果没有 - 我会提出 POST 请求并在 WebService1/WebService2.
中添加此设备你能给我建议吗,我应该使用什么模块,我应该如何开始这个比较?
例如 (file1)
<?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>
文件 2
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns3:networkdevice name="NEW-SW5" id="9a6ef750-2620-11e4-81be-b83861d71f95" xmlns:ns2="ers.ise.cisco.com" xmlns:ns3="network.ers.ise.cisco.com">
<link type="application/xml" href="https://hostname:9060/ers/config/networkdevice/123456" rel="self"/>
<authenticationSettings>
<enableKeyWrap>false</enableKeyWrap>
<keyInputFormat>ASCII</keyInputFormat>
<networkProtocol>RADIUS</networkProtocol>
<radiusSharedSecret>******</radiusSharedSecret>
</authenticationSettings>
<NetworkDeviceIPList>
<NetworkDeviceIP>
<ipaddress>10.66.12.128</ipaddress>
<mask>21</mask>
</NetworkDeviceIP>
</NetworkDeviceIPList>
<NetworkDeviceGroupList>
<NetworkDeviceGroup>Location#All Locations</NetworkDeviceGroup>
<NetworkDeviceGroup>Device Type#All Device Types</NetworkDeviceGroup>
</NetworkDeviceGroupList>
</ns3:networkdevice>
有一些特别之处:在文件 1 中,我的标签名为:deviceName、ipAddress,它们是 elements.
在 file2 中,我们有一个属性(因为它位于主要元素 ns3:networkdevice 中,它被称为 name ,它从 file1 响应我们的 deviceName )和另一个元素称为 ipaddress(文件 1 中的 ipAddress)
这不是一项从头开始编写的简单任务。你应该使用 XML::Compare
您可以使用 XML::Twig 来解析这两个响应。他们每个人都需要一个单独的解析器。
对于第一个,您需要选择 <deviceName>
和 <ipAddress>
这两个标签。匹配元素的简单 twig_handler
for each of them that access the text
属性 就足够了。
这些处理程序可能很复杂,但在我们的例子中,处理单个值的代码参考就足够了。我们知道每个值只有一次出现,所以我们可以直接将它们都赋值给各自的词法变量。
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>
对于第二个,您需要使用 att()
来获取其中一个元素的 name 属性,但这也很简单。
use strict;
use warnings;
use XML::Twig;
my ($device_name, $ip_address);
XML::Twig->new(
twig_handlers => {
'ns3:networkdevice' => sub { $device_name = $_->att('name') },
ipaddress => sub { $ip_address = $_->text },
}
)->parse(\*DATA);
say $device_name;
say $ip_address;
__DATA__
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns3:networkdevice name="NEW-SW5" id="9a6ef750-2620-11e4-81be-b83861d71f95" xmlns:ns2="ers.ise.cisco.com" xmlns:ns3="network.ers.ise.cisco.com">
<link type="application/xml" href="https://hostname:9060/ers/config/networkdevice/123456" rel="self"/>
<authenticationSettings>
<enableKeyWrap>false</enableKeyWrap>
<keyInputFormat>ASCII</keyInputFormat>
<networkProtocol>RADIUS</networkProtocol>
<radiusSharedSecret>******</radiusSharedSecret>
</authenticationSettings>
<NetworkDeviceIPList>
<NetworkDeviceIP>
<ipaddress>10.66.12.128</ipaddress>
<mask>21</mask>
</NetworkDeviceIP>
</NetworkDeviceIPList>
<NetworkDeviceGroupList>
<NetworkDeviceGroup>Location#All Locations</NetworkDeviceGroup>
<NetworkDeviceGroup>Device Type#All Device Types</NetworkDeviceGroup>
</NetworkDeviceGroupList>
</ns3:networkdevice>
现在你已经拥有了这两个,你可以将它们结合起来。我建议为它们中的每一个创建一个函数,传入响应 XML 并使它们 return 成为 $device_name
和 $ip_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
不是最好的选择。不要使用数字,而是使用 return 编辑响应的服务名称。
有了这两个函数,我们现在可以准确地检索我们想要的信息。剩下的就是检查它们了。
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 调用它,它会 return 一个真值,或者不是。
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 文件是 "the same" 的含义,目前还没有普遍的共识。例如,每个人都同意忽略开始和结束标记中的空格,属性周围的单引号和双引号之间的区别无关紧要,属性可以按任何顺序排列;但对于如何处理注释、元素标签之间的空格、命名空间前缀和许多其他细节,要求各不相同。
另一个要求不同的领域是当文件被认为不同时您需要什么信息。有些机制只会给你一个是或否的答案,不会帮助你找到差异。
这会导致可能存在通用解决方案,但它们可能并不总能满足您的特定要求。
因此,如果您准备编写几百行代码,那么编写自己的比较器并不是一个荒谬的想法。
但是如果您能在 Perl 环境中找到 运行 的示例,您可以考虑两个现成的解决方案:
XML canonicalizers:规范化两个文档,然后在二进制级别比较结果。
XPath 2.0: 提供函数 deep-equal() 来比较两个节点(包括文档节点)
很像 simbaque I'd use XML::Twig
,尽管我处理它的方式略有不同 - 我提供这个是为了比较 - 而不是使用 twig_handlers
- 我称之为强大的和有用的技术,但特别适合增量解析更大的 XML - 使用 get_xpath
在 XML 中查找基于 xpath
的引用的东西可能提供替代方案。
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $xml1 = XML::Twig->new->parsefile('test1a.xml');
my $xml2 = XML::Twig->new->parsefile('test1b.xml');
if ( $xml1->get_xpath( '//deviceName', 0 )->text
eq $xml2->root->att('name') )
{
print "Name matches\n";
}
if ( $xml1->get_xpath( '//ipAddress', 0 )->text
eq $xml2->get_xpath( '//ipaddress', 0 )->text )
{
print "IP matches\n";
}
我们将两个文件解析成一个XML::Twig
对象,然后使用get_xpath
查找节点位置。 //
表示树中的任何地方, 0
指的是哪个实例(例如第一个,唯一)。
理想情况下,我们可能会做一些 xpath 字符串来直接比较 - 我们不能在这里,因为 'name' 属性是根节点的属性(也是 XML::Twig
xpath引擎是你不能直接select属性内容)。
但是 XML::LibXML
- 功能更全面,代价是学习曲线有点陡峭。我一般不会使用它 但在这种特定情况下,它可以将 xpath
表达式处理为 select 根节点的属性。
所以会是这样的:
#!/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;
}