0

I'm using xmlunit 2.2.1 with Java and have the following XMLs to match. The problem here is twofold:

  1. the order is not the same
  2. some of the numerical values may have trailing zeroes (e.g. 0.250000)

For this reason ByNameAndTextRecSelector() doesn't work because it requires all the text nodes to be exact matches, which not the case here. I already implemented a DifferenceEvaluator that will take care of the trailing zeroes in the numbers. But what do I use to select the right elements to compare?

XML 1:

<test>
  <table>
    <row>
      <element>
        <code>ALPHA</code>
        <scale>0.25</scale>
      </element>
    </row>
    <row>
      <element>
        <code>DELTA</code>
        <scale>0.1</scale>
      </element>
    </row>
  </table>
</test>

XML 2:

<test>
  <table>
    <row>
      <element>
        <code>DELTA</code>
        <scale>0.1</scale>
      </element>
    </row>
    <row>
      <element>
        <code>ALPHA</code>
        <scale>0.2500000</scale>
      </element>
    </row>
  </table>
</test>
Roland
  • 7,525
  • 13
  • 61
  • 124

1 Answers1

1

Is the code element enough to select the correct nodes? If so, an approach like the user guide example https://github.com/xmlunit/user-guide/wiki/SelectingNodes#conditional-elementselectors should work.

ElementSelectors.conditionalBuilder()
    .whenElementIsNamed("row").thenUse(ElementSelectors.byXPath("./element/code", ElementSelectors.byNameAndText))
    .elseUse(ElementSelectors.byName)
    .build();

This will not help against the different number formats. To deal with them you must override the DifferenceEvaluator and detect the Text nodes that hold numbers, parse them and mark the differences as EQUAL if they are close enough.

If code is not enough, then you will need to write an ElementSelector that takes care of the numbers like the DifferenceEvaluator I described above and combine that with ElementSelectors.byNameAndText inside the conditional above.

Stefan Bodewig
  • 3,260
  • 15
  • 22
  • `code` is not enough, I will need `scale` too. How do I combine the two? Do I use `and` and put this inside the `thenUse`? – Roland Aug 12 '16 at 08:40
  • This seems to work: `and(byXPath("./element/code", byNameAndText), byXPath("./element/scale", new SelectorByNameAndNumber()))` – Roland Aug 12 '16 at 09:10
  • 1
    `and` should have worked if you wanted to keep them separate as well. Glad it worked. – Stefan Bodewig Aug 12 '16 at 09:30
  • I don't understand your last comment. Btw, I don't use the `.elseUse` but instead I put the other `ElementSelector` as arguments in the constructor of `DefaultNodeMatcher`. – Roland Aug 12 '16 at 09:46
  • Could you please clarify your last comment? Does it make a difference if instead of using the `.elseUse` I put the other `ElementSelector` as argument in the constructor of `DefaultNodeMatcher`? – Roland Aug 12 '16 at 12:38
  • 1
    sorry, I completely misread your comment and overlooked the `and` but thought you had combined the selectors for `scale` and `code` into one. – Stefan Bodewig Aug 13 '16 at 08:14
  • 1
    When using the varargs version of `DefaultNodeMatcher`'s constructor you need to be careful about order. `byName` would match arbitrary `row`s in your case, so it had to come last or your custom selector was never called. Whether it works as well as `elseUse` depends on whether your custom selector would match non-`row` elements as well. If it doesn't, then the varargs constructor would yield the same result. If it might match non-`row` elements as well, you may get unintended results. I prefer to make things explicit with the `conditionalBuilder`, though. – Stefan Bodewig Aug 13 '16 at 08:15