Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AttributeFilter with nodematcher #259

Closed
aceankit99 opened this issue Mar 10, 2023 · 6 comments
Closed

AttributeFilter with nodematcher #259

aceankit99 opened this issue Mar 10, 2023 · 6 comments
Labels

Comments

@aceankit99
Copy link

aceankit99 commented Mar 10, 2023

Hi Stefan,

I want to find out the difference between 2 xml file, ignoring few attribute of a node.

my XML-</AssignedContent> <AssignedContent ContentClass="FKN" IsRequired="false"> <ContentAttributes> <ContentAttribute AttributeName="PARAMETER-ABHAENGIGKEITe" CommentRequired="N" Description="Parameterabhängigkeit der Funktion" Group="FKN" LockedForGUI="R" MultipleValues="false" OnlyNormValues="false" Required="false" ShowNulls="true"> <ContentGrants/> </ContentAttribute> </ContentAttributes> </AssignedContent>

code which i tried-

` Diff diffs = DiffBuilder.compare(Source).withTest(Source2).ignoreComments().ignoreWhitespace()
.ignoreElementContentWhitespace().checkForSimilar()
.withNodeMatcher(new DefaultNodeMatcher(
ElementSelectors.conditionalBuilder().whenElementIsNamed("ContentAttributes")
.thenUse(ElementSelectors.byXPath("./ContentAttribute/text()",
ElementSelectors.byNameAndAttributes("Description")))
.elseUse(ElementSelectors.byNameAndAllAttributes).build()))
.withAttributeFilter(attr -> (attr.getName().equals("AttributeName"))

	    .withAttributeFilter(attr -> attr.getName().equals("AttributeName"))
	    .build();`

I tried with both withAttributeFilter and withDifferenceEvaluator but its not working. I wanted to ignore the AttributeName attribute of ContentAttribute .

Thanks

@bodewig
Copy link
Member

bodewig commented Mar 14, 2023

Sorry, the formatting of your example makes it almost impossible to see what you are looking for. In particular there is only one XML.

    .withAttributeFilter(attr -> attr.getName().equals("AttributeName"))

says: Only look at attributes named AttributeName, ignore all other attributes. I can only guess this is the exact opposite of what you want.

Unfortunately you don't say what result you get that you are not expecting - and also not what you tried "withDifferenceEvaluator", so it is hard to help here.

@aceankit99
Copy link
Author

Hi Stefan,

Thanks for the reply.I got the point, but i want to clear one doubt

` Diff diffs = DiffBuilder.compare(control).withTest(test).ignoreComments().ignoreWhitespace()
.ignoreElementContentWhitespace().checkForSimilar()

		  .withNodeMatcher(new DefaultNodeMatcher(
		    ElementSelectors.conditionalBuilder().whenElementIsNamed("VersionAttributes")
		    .thenUse(ElementSelectors.byXPath("./VersionAttribute/text()",
		      ElementSelectors.byNameAndAttributes("AttributeName")))
		    .elseUse(ElementSelectors.byNameAndAllAttributes).build()))`

Here what is the use of ElementSelectors.byNameAndAttributes("AttributeName"). The code is saying to compare by AttributeName or it has some different meaning.

@bodewig
Copy link
Member

bodewig commented Mar 16, 2023

Please see https://github.com/xmlunit/user-guide/wiki/SelectingNodes for a more comprehensive answer.

Your setup says:

Whenever XMLUnit looks at a list of elements and tries to decide which of the elements in the list that belongs to the control document to compare to which of the elements in the list of the test elements, then

  • if the XML element I'm looking at has the name "VersionAttributes" then grab the nested text of the first "VersionAttribute" child and apply the second element selector to it. I could explain what would happen then, but in your case nothing sensible will happen as the nested text is not an XML element at all. The XPath expression must yield an element, not a text. Let's assume for a moment the XPath yielded an element, then the second ElementSelector would be applied to the elements of both lists.
  • if the element has any other name then compare it to the element of the test list that has the same name and the same attribute values for all attributes

Say your control doc is

<root>
  <VersionAttributes>
    <VersionAttribute>1</VersionAttribute>
  <VersionAttributes>
  <a test="1"/>
  <a test="2" foo="bar"/>
  <a test="2"/>
  <VersionAttributes>
    <VersionAttribute>2</VersionAttribute>
  <VersionAttributes>
</root>

and your test document

<root>
  <VersionAttributes>
    <VersionAttribute>2</VersionAttribute>
  <VersionAttributes>
  <a test="2"/>
  <a test="2" foo="bar"/>
  <a test="1"/>
  <VersionAttributes>
    <VersionAttribute>1</VersionAttribute>
  <VersionAttributes>
</root>

then

  • the VersionAttributes children of either list will not get compared to anything at all as the XPath expression doesn't work.
  • the first control "a" will be compared to the last test "a" as this is the only one where all attributes match
  • the second control "a" will be compared to the second test "a". Even though the first test "a" matches on the "test" attribute, it doesn't match all attributes. ElementSelectors.byNameAndAttributes("test") would have picked the first test "a" as it is expected ot grab the first one with the same value in the "test" attribute and only the "test" attribute.

@aceankit99
Copy link
Author

Hi Stefan..thanks for the reply.

Lets say there is there are other attributes called "test2","test3" but it is not mentioned in the ElementSelectors.byNameAndAttributes("test").

so if i write this code
.withNodeMatcher(new DefaultNodeMatcher( ElementSelectors.conditionalBuilder().whenElementIsNamed("VersionAttributes") .thenUse(ElementSelectors.byXPath("./VersionAttribute", ElementSelectors.byNameAndAttributes("test"))) .elseUse(ElementSelectors.byNameAndAllAttributes).build())) .withAttributeFilter(attr->(attr.getName().equals("test")))

so, here the xmls should be matched using attribute "test" and all other attributes apart from test will be ignored as i have used .withAttributeFilter(attr->(attr.getName().equals("test"))),is that correct?

@bodewig
Copy link
Member

bodewig commented Mar 22, 2023

If you use ElementSelectors.byNameAndAttributes the only the listed attributes will be used to decide which elements to compare. It does not have any influence on whether differences are reported on the other attributes.

If you set an attribute filter, the attributes filtered out will be completely invisible to the comparison, so you should never see any differences related to these attributes.

I believe this is what you said, so yes, you are correct :-)

While reading your question I realized ElementSelectors (all of them) are completely unaware of attribute filters and always see all attributes. This is something I should document. And maybe I should add an byNameAndAllAttributes(Predicate<Attr>) that can be used if suppressed attributes might alter the node matching decision.

@aceankit99
Copy link
Author

Thanks Stefan for the explanation.

@bodewig bodewig closed this as completed Mar 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants