是否有一个Java库可以“比较”两个对象?

gojuced7  于 2023-03-06  发布在  Java
关注(0)|答案(8)|浏览(161)

是否有类似于Unix程序diff的Java实用程序库,但是对于对象呢?我正在寻找能够比较相同类型的两个对象并生成表示它们之间差异的数据结构的东西(并且可以递归地比较示例变量中的差异)。我不是在寻找文本diff的Java实现。我也不是正在寻求如何使用反射来完成此操作的帮助。
我正在维护的应用程序具有此功能的脆弱实现,其中有一些糟糕的设计选择,需要重写,但如果我们可以使用现成的东西,那将更好。
这里有一个我正在寻找的东西的例子:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffDataStructure diff = OffTheShelfUtility.diff(a, b);  // magical recursive comparison happens here

比较之后,实用程序会告诉我,“prop1”在两个对象之间是不同的,而“prop2”是相同的。我认为DiffDataStructure是树是最自然的,但是如果代码是可靠的,我就不会挑剔。

irtuqstp

irtuqstp1#

可能有点晚了,但我和你的情况一样,最终为你的用例创建了我自己的库。由于我被迫自己提出一个解决方案,我决定在Github上发布它,以节省其他人的辛苦工作。你可以在这里找到它:https://github.com/SQiShER/java-object-diff
---编辑--
下面是一个基于OP代码的小用法示例:

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

DiffNode diff = ObjectDifferBuilder.buildDefault().compare(a, b);

assert diff.hasChanges();
assert diff.childCount() == 1;
assert diff.getChild('prop1').getState() == DiffNode.State.CHANGED;
bz4sfanl

bz4sfanl2#

http://javers.org是一个库,它完全符合您的需要:具有返回Diff对象的compare(Object leftGraph,Object rightGraph)等方法。Diff包含更改列表(ReferenceChange,ValueChange,PropertyChange),例如

given:
DummyUser user =  dummyUser("id").withSex(FEMALE).build();
DummyUser user2 = dummyUser("id").withSex(MALE).build();
Javers javers = JaversTestBuilder.newInstance()

when:
Diff diff = javers.compare(user, user2)

then:
diff.changes.size() == 1
ValueChange change = diff.changes[0]
change.leftValue == FEMALE
change.rightValue == MALE

它可以处理图中的循环。
此外,你可以得到任何图形对象的快照。Javers有JSON序列化器和反序列化器来快照和更改,这样你就可以很容易地将它们保存在数据库中。有了这个库,你可以很容易地实现一个审计模块。

vsaztqbk

vsaztqbk3#

你也可以看看Apache的解决方案,大多数项目已经在类路径上有了它,因为它是commons-lang的一部分。
检查特定字段的差异:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/DiffBuilder.html
使用反射检查差异:
http://commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/apache/commons/lang3/builder/ReflectionDiffBuilder.html

pod7payv

pod7payv4#

所有的Javers库都只支持Java 7,我遇到了一个问题,因为我想把它用于Java 6项目,所以我碰巧取了源代码,并以一种适合Java 6的方式进行了更改,下面是github代码。
https://github.com/sand3sh/javers-forJava6
罐链接:https://github.com/sand3sh/javers-forJava6/blob/master/build/javers-forjava6.jar
我只改变了Java 7支持的“〈〉”固有的转换为Java 6支持,我不保证所有的功能都能工作,因为我已经为我注解了一些不必要的代码,它适用于所有自定义对象比较。

pqwbnv8z

pqwbnv8z5#

也许这会有帮助,这取决于你在哪里使用这段代码,它可能是有用的或有问题的。测试了这段代码。

/**
 * @param firstInstance
 * @param secondInstance
 */
protected static void findMatchingValues(SomeClass firstInstance,
        SomeClass secondInstance) {
    try {
        Class firstClass = firstInstance.getClass();
        Method[] firstClassMethodsArr = firstClass.getMethods();

        Class secondClass = firstInstance.getClass();
        Method[] secondClassMethodsArr = secondClass.getMethods();

        for (int i = 0; i < firstClassMethodsArr.length; i++) {
            Method firstClassMethod = firstClassMethodsArr[i];
            // target getter methods.
            if(firstClassMethod.getName().startsWith("get") 
                    && ((firstClassMethod.getParameterTypes()).length == 0)
                    && (!(firstClassMethod.getName().equals("getClass")))
            ){

                Object firstValue;
                    firstValue = firstClassMethod.invoke(firstInstance, null);

                logger.info(" Value "+firstValue+" Method "+firstClassMethod.getName());

                for (int j = 0; j < secondClassMethodsArr.length; j++) {
                    Method secondClassMethod = secondClassMethodsArr[j];
                    if(secondClassMethod.getName().equals(firstClassMethod.getName())){
                        Object secondValue = secondClassMethod.invoke(secondInstance, null);
                        if(firstValue.equals(secondValue)){
                            logger.info(" Values do match! ");
                        }
                    }
                }
            }
        }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
}
ryevplcw

ryevplcw6#

首先,我们必须将对象转换为贴图:

public Map<String, Object> objectToMap(Object object) throws JsonProcessingException {
    var mapper = new ObjectMapper();
    final var type = new TypeReference<HashMap<String, Object>>() {

    };
    ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
    return mapper.readValue(ow.writeValueAsString(object), type);
}

之后,将Map转换为平面Map:

public Map<String, Object> flatten(Map<String, Object> map) {
    return map.entrySet().stream()
        .flatMap(this::flatten)
        .collect(LinkedHashMap::new, (m, e) -> m.put(camelToUnderScore("/" + e.getKey()), e.getValue()),
            LinkedHashMap::putAll);
}

public Stream<Map.Entry<String, Object>> flatten(Map.Entry<String, Object> entry) {

    if (entry == null) {
        return Stream.empty();
    }

    if (entry.getValue() instanceof Map<?, ?>) {
        return ((Map<?, ?>) entry.getValue()).entrySet().stream()
            .flatMap(e -> flatten(
                new AbstractMap.SimpleEntry<>(camelToUnderScore(entry.getKey() + "/" + e.getKey()),
                    e.getValue())));
    }

    if (entry.getValue() instanceof List<?>) {
        List<?> list = (List<?>) entry.getValue();
        return IntStream.range(0, list.size())
            .mapToObj(i -> new AbstractMap.SimpleEntry<String, Object>(
                camelToUnderScore(entry.getKey() + "/" + i), list.get(i)))
            .flatMap(this::flatten);
    }

    return Stream.of(entry);
}

最后调用getDifferenceBetween2Maps来获取差异:

public Map<String, Object> getDifferenceBetween2Maps(final Map<String, Object> leftFlatMap,
                                                     final Map<String, Object> rightFlatMap) {
  
    final MapDifference<String, Object> difference = Maps.difference(leftFlatMap, rightFlatMap);

    var differencesList = new HashMap<String, Object>();
  
    differencesList.putAll(difference.entriesOnlyOnLeft());

    differencesList.putAll(difference.entriesOnlyOnRight());

    return differencesList;
}

使用示例:

Map<String, Object> oldObjectFlatMap = flatten(objectToMap(oldObject));
Map<String, Object> newObjectFlatMap = flatten(objectToMap(newObject));
var differencesList = getDifferenceBetween2Maps(oldObjectFlatMap , newObjectFlatMap);
jjhzyzn0

jjhzyzn07#

深入比较一个对象的所有属性的一个好方法是将它们转换为java.util.Map。这样,java.util.Map#equals将深入比较对象,工作就完成了!
唯一的问题是将对象转换为Map。一种方法是使用带有org.codehaus.jackson.map.ObjectMapper的reflexion。
因此,存在一个来自com.google.common.collect.MapDifference的工具,用于描述两个Map之间的差异。

SomeClass a = new SomeClass();
SomeClass b = new SomeClass();

a.setProp1("A");
a.setProp2("X");

b.setProp1("B");
b.setProp2("X");

// Convert object to Map
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> aMap =  objectMapper.convertValue(a, Map.class);
Map<String, Object> bMap =  objectMapper.convertValue(b, Map.class);

aMap.equals(bMap); // --> false

// Show deeply all differences
MapDifference<String, Object> diff = Maps.difference(aMap, bMap);
wn9m85ua

wn9m85ua8#

快速判断两个对象是否不同的更简单方法是使用apache commons库

BeanComparator lastNameComparator = new BeanComparator("lname");
    logger.info(" Match "+bc.compare(firstInstance, secondInstance));

相关问题