Skip Menu |

This queue is for tickets about the XML-SemanticDiff CPAN distribution.

Report information
The Basics
Id: 39705
Status: resolved
Priority: 0/
Queue: XML-SemanticDiff

People
Owner: Nobody in particular
Requestors: eshikov [...] mail.ru
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 0.98
Fixed in: 0.98



Subject: Exclude some paths from
I've created small patch which allows to ignore some paths in comparison it allows to call my $diff = XML::SemanticDiff->new(ignore_xpath=>['/xml/z']); And SemanticDiff created in this way will ignore nodes matching xpath /xml/z during comparison. It's also possible to give more then one path. This feature is pretty useful to compare soap:message with timestamp headers for example
Subject: ignore_xpath.diff
85a86,106 > > > # Very simple xpath comparison function drop off [] and namespaces then split / and compare by elements > sub match_xpath { > my $self = shift; > my ($xpath, $flat_name) = @_; > my @x_way = split /\//, $xpath; > my @f_way = split /\//, $flat_name; > for my $i(0..$#x_way) { > $x_way[$i]=~s/.*?://g; > } > for my $i(0..$#f_way) { > $f_way[$i]=~s/\[.*?\]$//g; > } > return 0 if $#x_way > $#f_way; > for my $i(0..$#x_way) { > return 0 if($x_way[$i] ne $f_way[$i]) > } > return 1; > } > 103a125,136 > if(defined $self->{ignore_xpath}) { > my $ignore = $self->{ignore_xpath}; > my @ignore = @$ignore; > for my $path(@ignore) { > for my $ref(($from_doc, $to_doc)) { > for my $key(keys %$ref) { > delete $ref->{$key} if $self->match_xpath($path, $key); > } > } > } > } > 108d140 < 170c202 < else { --- > else {
On Tue Sep 30 06:59:44 2008, bskaplou wrote: Show quoted text
> I've created small patch which allows to ignore some paths in
comparison Show quoted text
> it allows to call > my $diff = XML::SemanticDiff->new(ignore_xpath=>['/xml/z']); > And SemanticDiff created in this way will ignore nodes matching
xpath Show quoted text
> /xml/z during comparison. > It's also possible to give more then one path. > This feature is pretty useful to compare soap:message with timestamp > headers for example
Hi! Thanks for your patch. However, it is unusable, as it is. Please checkout: http://svn.berlios.de/svnroot/repos/web-cpan/XML-SemanticDiff/trunk/ And use svn diff to prepare the patch. Moreover, you need to include a few regression tests for the new features under t/. Regards, -- Shlomi Fish
From: eshikov [...] mail.ru
Hello Shlomi, Thanks, Boris. On Tue Sep 30 08:33:54 2008, SHLOMIF wrote: Show quoted text
> On Tue Sep 30 06:59:44 2008, bskaplou wrote:
> > I've created small patch which allows to ignore some paths in
> comparison
> > it allows to call > > my $diff = XML::SemanticDiff->new(ignore_xpath=>['/xml/z']); > > And SemanticDiff created in this way will ignore nodes matching
> xpath
> > /xml/z during comparison. > > It's also possible to give more then one path. > > This feature is pretty useful to compare soap:message with timestamp > > headers for example
> > Hi! > > Thanks for your patch. However, it is unusable, as it is. Please > checkout: > > http://svn.berlios.de/svnroot/repos/web-cpan/XML-SemanticDiff/trunk/ > > And use svn diff to prepare the patch. Moreover, you need to include a > few regression tests for the new features under t/. > > Regards, > > -- Shlomi Fish
Index: t/14ignore_xpath.t =================================================================== --- t/14ignore_xpath.t (revision 0) +++ t/14ignore_xpath.t (revision 0) @@ -0,0 +1,35 @@ +use strict; +use warnings; + +use Test::More tests => 2; + +use XML::SemanticDiff; + +my $xml1 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +</root> +EOX + +my $xml2 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el3/> +</root> +EOX + +my $diff_simple = XML::SemanticDiff->new(); +my $diff_ignore = XML::SemanticDiff->new(ignore_xpath=>["/root/el3"]); + +#TEST +my @results = $diff_simple->compare($xml1, $xml2); +ok(@results, "XMLs are not identical"); + +#TEST +@results = $diff_ignore->compare($xml1, $xml2); +ok((!@results), "XMLs should count identical if xpath /root/el3 is excluded"); + Index: t/15ignore_multi.t =================================================================== --- t/15ignore_multi.t (revision 0) +++ t/15ignore_multi.t (revision 0) @@ -0,0 +1,41 @@ +use strict; +use warnings; + +use Test::More tests => 3; + +use XML::SemanticDiff; + +my $xml1 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el4/> +</root> +EOX + +my $xml2 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el3/> +</root> +EOX + +my $diff_simple = XML::SemanticDiff->new(); +my $diff_ignore = XML::SemanticDiff->new(ignore_xpath=>["/root/el3"]); +my $diff_multiignore = XML::SemanticDiff->new(ignore_xpath=>["/root/el3", "/root/el4"]); + +#TEST +my @results = $diff_simple->compare($xml1, $xml2); +ok($#results == 1, "Two differences in XMLs"); + +#TEST +@results = $diff_ignore->compare($xml1, $xml2); +ok($#results == 0, "Only one difference if /root/el3 is excluded"); + +#TEST +@results = $diff_multiignore->compare($xml1, $xml2); +ok((!@results), "XMLs should count identical if xpaths /root/el3 and /root/el4 are excluded"); + Index: lib/XML/SemanticDiff.pm =================================================================== --- lib/XML/SemanticDiff.pm (revision 3238) +++ lib/XML/SemanticDiff.pm (working copy) @@ -83,6 +83,25 @@ return 0; } } + +sub match_xpath { + my $self = shift; + my ($xpath, $flat_name) = @_; + my @x_way = split /\//, $xpath; + my @f_way = split /\//, $flat_name; + for my $i(0..$#x_way) { + $x_way[$i]=~s/.*?://g; + } + for my $i(0..$#f_way) { + $f_way[$i]=~s/\[.*?\]$//g; + } + return 0 if $#x_way > $#f_way; + for my $i(0..$#x_way) { + return 0 if($x_way[$i] ne $f_way[$i]) + } + return 1; +} + # Okay, it's pretty basic... # # We flatten each doc tree to a Perl hash where the keys are "fully qualified" @@ -105,7 +124,20 @@ my $handler = $self->{diffhandler} || XML::SemanticDiff::BasicHandler->new(%$self); + if(defined $self->{ignore_xpath}) { + my $ignore = $self->{ignore_xpath}; + my @ignore = @$ignore; + for my $path(@ignore) { + for my $ref(($from_doc, $to_doc)) { + for my $key(keys %$ref) { + delete $ref->{$key} if $self->match_xpath($path, $key); + } + } + } + } + + # fire the init handler push (@warnings, $handler->init($self)) if $handler->can('init');
From: eshikov [...] mail.ru
Hello Shlomi, Attached file is svn diff with latest version, it also contains two unit tests. I've run all existing tests and two new they all passes. Thanks, Boris. On Tue Sep 30 08:33:54 2008, SHLOMIF wrote: Show quoted text
> On Tue Sep 30 06:59:44 2008, bskaplou wrote:
> > I've created small patch which allows to ignore some paths in
> comparison
> > it allows to call > > my $diff = XML::SemanticDiff->new(ignore_xpath=>['/xml/z']); > > And SemanticDiff created in this way will ignore nodes matching
> xpath
> > /xml/z during comparison. > > It's also possible to give more then one path. > > This feature is pretty useful to compare soap:message with timestamp > > headers for example
> > Hi! > > Thanks for your patch. However, it is unusable, as it is. Please > checkout: > > http://svn.berlios.de/svnroot/repos/web-cpan/XML-SemanticDiff/trunk/ > > And use svn diff to prepare the patch. Moreover, you need to include a > few regression tests for the new features under t/. > > Regards, > > -- Shlomi Fish
Index: t/14ignore_xpath.t =================================================================== --- t/14ignore_xpath.t (revision 0) +++ t/14ignore_xpath.t (revision 0) @@ -0,0 +1,35 @@ +use strict; +use warnings; + +use Test::More tests => 2; + +use XML::SemanticDiff; + +my $xml1 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +</root> +EOX + +my $xml2 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el3/> +</root> +EOX + +my $diff_simple = XML::SemanticDiff->new(); +my $diff_ignore = XML::SemanticDiff->new(ignore_xpath=>["/root/el3"]); + +#TEST +my @results = $diff_simple->compare($xml1, $xml2); +ok(@results, "XMLs are not identical"); + +#TEST +@results = $diff_ignore->compare($xml1, $xml2); +ok((!@results), "XMLs should count identical if xpath /root/el3 is excluded"); + Index: t/15ignore_multi.t =================================================================== --- t/15ignore_multi.t (revision 0) +++ t/15ignore_multi.t (revision 0) @@ -0,0 +1,41 @@ +use strict; +use warnings; + +use Test::More tests => 3; + +use XML::SemanticDiff; + +my $xml1 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el4/> +</root> +EOX + +my $xml2 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el3/> +</root> +EOX + +my $diff_simple = XML::SemanticDiff->new(); +my $diff_ignore = XML::SemanticDiff->new(ignore_xpath=>["/root/el3"]); +my $diff_multiignore = XML::SemanticDiff->new(ignore_xpath=>["/root/el3", "/root/el4"]); + +#TEST +my @results = $diff_simple->compare($xml1, $xml2); +ok($#results == 1, "Two differences in XMLs"); + +#TEST +@results = $diff_ignore->compare($xml1, $xml2); +ok($#results == 0, "Only one difference if /root/el3 is excluded"); + +#TEST +@results = $diff_multiignore->compare($xml1, $xml2); +ok((!@results), "XMLs should count identical if xpaths /root/el3 and /root/el4 are excluded"); + Index: lib/XML/SemanticDiff.pm =================================================================== --- lib/XML/SemanticDiff.pm (revision 3238) +++ lib/XML/SemanticDiff.pm (working copy) @@ -83,6 +83,25 @@ return 0; } } + +sub match_xpath { + my $self = shift; + my ($xpath, $flat_name) = @_; + my @x_way = split /\//, $xpath; + my @f_way = split /\//, $flat_name; + for my $i(0..$#x_way) { + $x_way[$i]=~s/.*?://g; + } + for my $i(0..$#f_way) { + $f_way[$i]=~s/\[.*?\]$//g; + } + return 0 if $#x_way > $#f_way; + for my $i(0..$#x_way) { + return 0 if($x_way[$i] ne $f_way[$i]) + } + return 1; +} + # Okay, it's pretty basic... # # We flatten each doc tree to a Perl hash where the keys are "fully qualified" @@ -105,7 +124,20 @@ my $handler = $self->{diffhandler} || XML::SemanticDiff::BasicHandler->new(%$self); + if(defined $self->{ignore_xpath}) { + my $ignore = $self->{ignore_xpath}; + my @ignore = @$ignore; + for my $path(@ignore) { + for my $ref(($from_doc, $to_doc)) { + for my $key(keys %$ref) { + delete $ref->{$key} if $self->match_xpath($path, $key); + } + } + } + } + + # fire the init handler push (@warnings, $handler->init($self)) if $handler->can('init');
Hi Boris! Sorry for the late response - I encountered some technical difficulties. On Wed Oct 01 02:45:01 2008, bskaplou wrote: Show quoted text
> Hello Shlomi, > > Attached file is svn diff with latest version, it also contains two
unit Show quoted text
> tests. I've run all existing tests and two new they all passes. >
Some notes on your patch: 1. {{{ ok($#results == 1, "Two differences in XMLs"); + +#TEST +@results = $diff_ignore->compare($xml1, $xml2); +ok($#results == 0, "Only one difference if /root/el3 is excluded"); }}} Please compare scalar(@results) using is. 2. In the test files, put the tests inside blocks for localising everything: <<< { my $diff = XML::SemanticDiff->(...) } Show quoted text
>>>
3. Use "for my $i (0...)" (with a space after the $i" instead of "for my $i(0....)" without it. 4. Don't use "STMT if ..." - instead write it as "if ( ... ) { STMt } " 5. <<<< my $ignore = $self->{ignore_xpath}; + my @ignore = @$ignore; Show quoted text
>>>>
Just do for my $path (@$ignore) - don't declare a variable - @ignore. 6. for my $ref(($from_doc, $to_doc)) - just do "for my $ref ($from_doc, $to_doc)" 7. Finally, this feature needs documentation. Regards, -- Shlomi Fish Show quoted text
> Thanks, > Boris. >
From: eshikov [...] mail.ru
Hello Shlomi, I made style improvements and added few string describing new option to documentation. Hope this version is better then prev ones. Regards, Boris. On Wed Oct 01 14:07:25 2008, SHLOMIF wrote: Show quoted text
> Hi Boris! > > Sorry for the late response - I encountered some technical > difficulties. > > On Wed Oct 01 02:45:01 2008, bskaplou wrote:
> > Hello Shlomi, > > > > Attached file is svn diff with latest version, it also contains two
> unit
> > tests. I've run all existing tests and two new they all passes. > >
> > Some notes on your patch: > > 1. > {{{ > ok($#results == 1, "Two differences in XMLs"); > + > +#TEST > +@results = $diff_ignore->compare($xml1, $xml2); > +ok($#results == 0, "Only one difference if /root/el3 is excluded"); > }}} > > Please compare scalar(@results) using is. > > 2. In the test files, put the tests inside blocks for localising > everything: > > <<< > { > my $diff = XML::SemanticDiff->(...) > }
> >>>
> > 3. Use "for my $i (0...)" (with a space after the $i" instead of "for > my $i(0....)" without it. > > 4. Don't use "STMT if ..." - instead write it as "if ( ... ) { > STMt } " > > 5. > > <<<< > my $ignore = $self->{ignore_xpath}; > + my @ignore = @$ignore;
> >>>>
> > Just do for my $path (@$ignore) - don't declare a variable - @ignore. > > 6. for my $ref(($from_doc, $to_doc)) - just do "for my $ref > ($from_doc, $to_doc)" > > 7. Finally, this feature needs documentation. > > Regards, > > -- Shlomi Fish > > >
> > Thanks, > > Boris. > >
Index: t/14ignore_xpath.t =================================================================== --- t/14ignore_xpath.t (revision 0) +++ t/14ignore_xpath.t (revision 0) @@ -0,0 +1,39 @@ +use strict; +use warnings; + +use Test::More tests => 2; + +use XML::SemanticDiff; + +my $xml1 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +</root> +EOX + +my $xml2 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el3/> +</root> +EOX + + +#TEST +{ + my $diff_simple = XML::SemanticDiff->new(); + my @results = $diff_simple->compare($xml1, $xml2); + ok(@results, "XMLs are not identical"); +} + +#TEST +{ + my $diff_ignore = XML::SemanticDiff->new(ignorexpath=>["/root/el3"]); + my @results = $diff_ignore->compare($xml1, $xml2); + ok((!@results), "XMLs should count identical if xpath /root/el3 is excluded"); +} + Index: t/15ignore_multi.t =================================================================== --- t/15ignore_multi.t (revision 0) +++ t/15ignore_multi.t (revision 0) @@ -0,0 +1,45 @@ +use strict; +use warnings; + +use Test::More tests => 3; + +use XML::SemanticDiff; + +my $xml1 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el4/> +</root> +EOX + +my $xml2 = <<'EOX'; +<?xml version="1.0"?> +<root> +<el1 el1attr="good"/> +<el2 el2attr="good">Some Text</el2> +<el3/> +</root> +EOX + +#TEST +{ + my $diff_simple = XML::SemanticDiff->new(); + my $results = $diff_simple->compare($xml1, $xml2); + ok($results == 2, "Two differences in XMLs"); +} + +#TEST +{ + my $diff_ignore = XML::SemanticDiff->new(ignorexpath=>["/root/el3"]); + my $results = $diff_ignore->compare($xml1, $xml2); + ok($results == 1, "Only one difference if /root/el3 is excluded"); +} + +#TEST +{ + my $diff_multiignore = XML::SemanticDiff->new(ignorexpath=>["/root/el3", "/root/el4"]); + my $results = $diff_multiignore->compare($xml1, $xml2); + ok($results == 0, "XMLs should count identical if xpaths /root/el3 and /root/el4 are excluded"); +} Index: lib/XML/SemanticDiff.pm =================================================================== --- lib/XML/SemanticDiff.pm (revision 3238) +++ lib/XML/SemanticDiff.pm (working copy) @@ -83,6 +83,27 @@ return 0; } } + +sub match_xpath { + my $self = shift; + my ($xpath, $flat_name) = @_; + my @x_way = split /\//, $xpath; + my @f_way = split /\//, $flat_name; + for my $i (0..$#x_way) { + $x_way[$i]=~s/.*?://g; + } + for my $i (0..$#f_way) { + $f_way[$i]=~s/\[.*?\]$//g; + } + return 0 if $#x_way > $#f_way; + for my $i (0..$#x_way) { + if ($x_way[$i] ne $f_way[$i]) { + return 0; + } + } + return 1; +} + # Okay, it's pretty basic... # # We flatten each doc tree to a Perl hash where the keys are "fully qualified" @@ -105,6 +126,19 @@ my $handler = $self->{diffhandler} || XML::SemanticDiff::BasicHandler->new(%$self); + # drop away nodes matching xpaths to be ignored + if (defined $self->{ignorexpath}) { + my $ignore = $self->{ignorexpath}; + for my $path (@$ignore) { + for my $ref ($from_doc, $to_doc) { + for my $key (keys %$ref) { + if ($self->match_xpath($path, $key)) { + delete $ref->{$key}; + } + } + } + } + } # fire the init handler push (@warnings, $handler->init($self)) if $handler->can('init'); @@ -470,6 +504,12 @@ Please see the section on 'CUSTOM HANDLERS' below. +=item * ignorexpath + +This option takes array of strings as argument. Strings are interpreted as simple xpath expressions. Nodes matching these expressions are ignored during comparison. All xpath expressions should be absolute (start with '/'). + +Current implementation ignores namespaces during comparison. + =back =head2 @results = $differ->compare($xml1, $xml2)
Thanks! This patch is now included in XML-SemanticDiff-0.99 which was uploaded to CPAN now.