如何递归地比较相同但未知类型的两个Java对象的字段值?

如何递归地比较相同但未知类型的两个Java对象的字段值?,java,xml,reflection,jaxb,Java,Xml,Reflection,Jaxb,我们正在使用JAXB将XML配置文件解析为Java对象。XML文件是版本化的,在将版本1.0和2.0加载到对象中之后,我们希望递归地比较相同但未知类型的两个对象(对于所有类型的对象有许多不同的配置)及其字段值,并打印出差异 final EqualsStrategy strategy = new org.jvnet.hyperjaxb3.lang.builder.ExtendedJAXBEqualsStrategy() { @Override public

我们正在使用JAXB将XML配置文件解析为Java对象。XML文件是版本化的,在将版本1.0和2.0加载到对象中之后,我们希望递归地比较相同但未知类型的两个对象(对于所有类型的对象有许多不同的配置)及其字段值,并打印出差异

    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;
            }
        }

    };
对象可能如下所示

@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;
    }

}
@XmlRootElement(name=“HelloWorld”)
公共类HelloWorldConfiguration{
私有列表helloWorldObjects=new ArrayList();
公共HelloWorldConfiguration(){
HelloWorldObject o=新的HelloWorldObject();
helloWorldObjects.add(o);
helloWorldObjects.add(o);
helloWorldObjects.add(o);
helloWorldObjects.add(o);
helloWorldObjects.add(o);
}
@XmlElement(name=“helloWorldObject”)
公共列表getHelloWorldObjects(){
返回helloWorldObjects;
}
public void setHelloWorldObjects(列出helloWorldObjects){
this.helloWorldObjects=helloWorldObjects;
}
}
公共类HelloWorldObject{
私人舞台名=新舞台(“托尼”);
私人舞台secondName=新舞台(“斯塔克”);
公共阶段getFirstName(){
返回名字;
}
public void setFirstName(Stage firstName){
this.firstName=firstName;
}
公共阶段getSecondName(){
返回secondName;
}
public void setSecondName(Stage secondName){
this.secondName=secondName;
}
}
例如,我们希望了解有关上述HelloWorldConfiguration对象的以下更改

  • 列表中还有其他“HelloWorldObject”项(必须在屏幕上打印带有属性的项)
  • 位置n处的“HelloWorldObject”有一个新的“firstName”值(已更改的字段或XML元素的名称及其值应打印出来)
  • 新的“HelloWorldObject”列表缩短了以下2个元素(缺少的元素必须与所有属性和值一起打印)
我的问题如下

@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;
    }

}
  • 您是在Java对象级别上通过反射来解决这个问题,还是比较两个不同的XML文件
  • 有没有图书馆已经为我做了类似的事情?在XML还是Java对象级别
  • 有什么例子吗

免责声明。我是插件包的作者,其中包括


如果从XML模式生成类,则在本用例中可能会用到

能够生成对JAXB类实例进行深层结构遍历值比较的
equals
方法:

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;
}
我希望你有这个想法。您可以提供一个“定位器”,用于跟踪被比较对象的位置,并提供一个用于比较各个值的策略

因此,您可以:

  • 深入比较模式派生的JAXB类实例
  • 了解不同之处(精确值)
  • 知道差异在哪里(在对象结构中的确切位置)
整个过程没有反射,因此速度相当快

下面是一段来自。这是我在其中一个测试中比较对象“之前”和“之后”,并记录差异

    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;
            }
        }

    };

从另一方面来说,这种方法并不像你从风投那里知道的那样是一种真正的“差别”。它只是说有些东西是不同的,但没有计算任何“最短编辑距离”。

谷歌搜索“xml diff java”找到了这样一个答案:实际上很少有用例需要反射。那些处理真正未知数据的。考虑使用A来确定对象具有哪些特定配置。如果对象包含其他类似的对象,您只需递归地执行此操作,直到遍历整个对象图。这种方法不需要我知道对象由哪些字段组成(字段的确切数据类型和名称)以及对象的类型吗?@TonyStark不,我不这么认为。请参阅答案中的最后一个代码段-没有提到特定的类型或字段。然而,这种方法仅限于模式派生的类。当通过JAXB2 Equals插件进行增强时,它们实现了
Equals
接口。这是一个先决条件。但在比较时,您不需要知道确切的类型、值和字段。