How could I recursively compare the field values ​​of two Java objects of the same but unknown type?

We parse XML configuration files from JAXB into Java objects. Version XML files and after loading versions 1.0 and 2.0 into objects, we would like to compare two objects of the same unknown type (many different configurations for all kinds of things) recursively and their field values ​​and print the differences.

The object might look like this.

@XmlRootElement(name = "HelloWorld")
public class HelloWorldConfiguration {
    private List<HelloWorldObject> helloWorldObjects = new ArrayList<HelloWorldObject>();

    public HelloWorldConfiguration() {
        HelloWorldObject o = new HelloWorldObject();
        helloWorldObjects.add(o);
        helloWorldObjects.add(o);
        helloWorldObjects.add(o);
        helloWorldObjects.add(o);
        helloWorldObjects.add(o);
    }

    @XmlElement(name = "helloWorldObject")
    public List<HelloWorldObject> getHelloWorldObjects() {
        return helloWorldObjects;
    }

    public void setHelloWorldObjects(List<HelloWorldObject> helloWorldObjects) {
        this.helloWorldObjects = helloWorldObjects;
    }
}

public class HelloWorldObject {
    private Stage firstName = new Stage("Tony");
    private Stage secondName = new Stage("Stark");

    public Stage getFirstName() {
        return firstName;
    }

    public void setFirstName(Stage firstName) {
        this.firstName = firstName;
    }

    public Stage getSecondName() {
        return secondName;
    }

    public void setSecondName(Stage secondName) {
        this.secondName = secondName;
    }

}

      

For example, would we like to be informed about the following changes to the above HelloWorldConfiguration object?

  • there is an additional element in the list "HelloWorldObject" (the element with its attibuutes should be printed on the screen)
  • "HelloWorldObject" at position n has the new value "firstName" (the field name or XML element that was changed and its value should be printed).
  • the new "HelloWorldObject" list is shorter by the next two elements (missing elements must be printed with all attributes and values)

My questions are as follows.

  • Could you solve this with Java object level reflection or compare two different XML files?
  • Are there libraries out there that already do something like this for me? At the XML or Java object level?
  • Any examples?
+3


source to share


1 answer


Denial of responsibility. I am the author of the JAXB2 Basics plugin which includes the JAXB2 Equals plugin .


If you are creating your classes from XML Schema, the JAXB2 Equals plugin can be useful for you in this case.

The JAXB2 Equals plugin is capable of generating methods equals

that perform deep comparisons of the structure values ​​of JAXB class instances:

public boolean equals(Object object) {
    final EqualsStrategy strategy = JAXBEqualsStrategy.INSTANCE;
    return equals(null, null, object, strategy);
}

public boolean equals(ObjectLocator thisLocator, ObjectLocator thatLocator, Object object, EqualsStrategy strategy) {
    if (!(object instanceof PurchaseOrderType)) {
        return false;
    }
    if (this == object) {
        return true;
    }
    final PurchaseOrderType that = ((PurchaseOrderType) object);
    {
        USAddress lhsShipTo;
        lhsShipTo = this.getShipTo();
        USAddress rhsShipTo;
        rhsShipTo = that.getShipTo();
        if (!strategy.equals(LocatorUtils.property(thisLocator, "shipTo", lhsShipTo), LocatorUtils.property(thatLocator, "shipTo", rhsShipTo), lhsShipTo, rhsShipTo)) {
            return false;
        }
    }
    {
        USAddress lhsBillTo;
        lhsBillTo = this.getBillTo();
        USAddress rhsBillTo;
        rhsBillTo = that.getBillTo();
        if (!strategy.equals(LocatorUtils.property(thisLocator, "billTo", lhsBillTo), LocatorUtils.property(thatLocator, "billTo", rhsBillTo), lhsBillTo, rhsBillTo)) {
            return false;
        }
    }
    // ...
    return true;
}

      

Hope you have an idea. You can provide a "locator" that will track the location of things being compared and a strategy that will compare the individual values.



As a result, you can:

  • Do an in-depth comparison of schema-based JAXB instances.
  • Know what is different (exact values).
  • Know where the differences are (exact location in the object structure).

And all this is trouble-free and therefore pretty fast.

Below is a snippet from another project . This is from one of the tests where I compare the "before" and "after" object and write down the differences.

    final EqualsStrategy strategy = new org.jvnet.hyperjaxb3.lang.builder.ExtendedJAXBEqualsStrategy() {

        @Override
        public boolean equals(ObjectLocator leftLocator,
                ObjectLocator rightLocator, Object lhs, Object rhs) {
            if (!super.equals(leftLocator, rightLocator, lhs, rhs)) {
                logger.debug("Objects are not equal.");
                super.equals(leftLocator, rightLocator, lhs, rhs);
                logger.debug("Left: "
                        + (lhs == null ? "null" : lhs.toString()));
                if (leftLocator != null) {
                    logger.debug("At [" + leftLocator.getPathAsString()
                            + "].");
                }
                logger.debug("Right: "
                        + (rhs == null ? "null" : rhs.toString()));
                if (rightLocator != null) {
                    logger.debug("At [" + rightLocator.getPathAsString()
                            + "].");
                }
                return false;
            } else

            {
                return true;
            }
        }

    };

      

On the other hand, this approach is not a real "difference" as you can tell from the VCS. It only says something else, but does not calculate the "shortest edit distance".

+1


source







All Articles