简体   繁体   English

java中如何合并两个复杂的对象

[英]How to merge two complex objects in java

I have two java objects and I want to merge them into single object.我有两个 java 对象,我想将它们合并为单个对象。 Problem is the two objects does not contain plain primitive type properties(fields) they contain complex type properties(like object of other type and list of objects of other type).问题是这两个对象不包含普通的原始类型属性(字段),它们包含复杂类型的属性(如其他类型的对象和其他类型的对象列表)。

Object 1: Returns by setting up some properties (fields) and对象 1:通过设置一些属性(字段)和返回

Objects 2: returns by setting up some properties (fields) or it may return new objects of the type which it holds but not returned by object 1.对象 2:通过设置一些属性(字段)返回,或者它可能返回它所拥有但不是对象 1 返回的类型的新对象。

Both object 1 and object 2 are of same type.对象 1 和对象 2 的类型相同。

Result Object 3 = obj1 properties + update the properties from obj 2 if same type as obj1 + new updated object from obj2结果对象 3 = obj1 属性 + 如果与 obj1 的类型相同,则更新来自 obj 2 的属性 + 来自 obj2 的新更新对象

Its pretty easy to do using the org.springframework.beans.BeanUtils class provided by spring.使用 spring 提供的org.springframework.beans.BeanUtils类很容易做到。 Or the Apache Commons BeanUtils library which I believe Springs version is either based on or is the same as.或者我认为 Springs 版本基于或相同的Apache Commons BeanUtils 库

public static <T> T combine2Objects(T a, T b) throws InstantiationException, IllegalAccessException {
    // would require a noargs constructor for the class, maybe you have a different way to create the result.
    T result = (T) a.getClass().newInstance();
    BeanUtils.copyProperties(a, result);
    BeanUtils.copyProperties(b, result);
    return result;
}

if you cant or dont have a noargs constructor maybe you just pass in the result如果你不能或没有 noargs 构造函数,也许你只是传入结果

public static <T> T combine2Objects(T a, T b, T destination) {
    BeanUtils.copyProperties(a, destination);
    BeanUtils.copyProperties(b, destination);
    return destination;
}

If you dont want null properties being copied you can use something like this:如果你不想复制空属性,你可以使用这样的东西:

public static void nullAwareBeanCopy(Object dest, Object source) throws IllegalAccessException, InvocationTargetException {
    new BeanUtilsBean() {
        @Override
        public void copyProperty(Object dest, String name, Object value)
                throws IllegalAccessException, InvocationTargetException {
            if(value != null) {
                super.copyProperty(dest, name, value);
            }
        }
    }.copyProperties(dest, source);
}

Nested object solution嵌套对象解决方案

Heres a bit more robust solution.这是一个更强大的解决方案。 It supports nested object copying, objects 1+ level deep will no longer be copied by reference, instead Nested objects will be cloned or their properties be copied individually.支持嵌套对象复制,1+级深度的对象将不再通过引用复制,而是复制嵌套对象或单独复制它们的属性。

/**
 * Copies all properties from sources to destination, does not copy null values and any nested objects will attempted to be
 * either cloned or copied into the existing object. This is recursive. Should not cause any infinite recursion.
 * @param dest object to copy props into (will mutate)
 * @param sources
 * @param <T> dest
 * @return
 * @throws IllegalAccessException
 * @throws InvocationTargetException
 */
public static <T> T copyProperties(T dest, Object... sources) throws IllegalAccessException, InvocationTargetException {
    // to keep from any chance infinite recursion lets limit each object to 1 instance at a time in the stack
    final List<Object> lookingAt = new ArrayList<>();

    BeanUtilsBean recursiveBeanUtils = new BeanUtilsBean() {

        /**
         * Check if the class name is an internal one
         * @param name
         * @return
         */
        private boolean isInternal(String name) {
            return name.startsWith("java.") || name.startsWith("javax.")
                    || name.startsWith("com.sun.") || name.startsWith("javax.")
                    || name.startsWith("oracle.");
        }

        /**
         * Override to ensure that we dont end up in infinite recursion
         * @param dest
         * @param orig
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        @Override
        public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
            try {
                // if we have an object in our list, that means we hit some sort of recursion, stop here.
                if(lookingAt.stream().anyMatch(o->o == dest)) {
                    return; // recursion detected
                }
                lookingAt.add(dest);
                super.copyProperties(dest, orig);
            } finally {
                lookingAt.remove(dest);
            }
        }

        @Override
        public void copyProperty(Object dest, String name, Object value)
                throws IllegalAccessException, InvocationTargetException {
            // dont copy over null values
            if (value != null) {
                // attempt to check if the value is a pojo we can clone using nested calls
                if(!value.getClass().isPrimitive() && !value.getClass().isSynthetic() && !isInternal(value.getClass().getName())) {
                    try {
                        Object prop = super.getPropertyUtils().getProperty(dest, name);
                        // get current value, if its null then clone the value and set that to the value
                        if(prop == null) {
                            super.setProperty(dest, name, super.cloneBean(value));
                        } else {
                            // get the destination value and then recursively call
                            copyProperties(prop, value);
                        }
                    } catch (NoSuchMethodException e) {
                        return;
                    } catch (InstantiationException e) {
                        throw new RuntimeException("Nested property could not be cloned.", e);
                    }
                } else {
                    super.copyProperty(dest, name, value);
                }
            }
        }
    };


    for(Object source : sources) {
        recursiveBeanUtils.copyProperties(dest, source);
    }

    return dest;
}

Its kinda quick and dirty but works well.它有点快速和肮脏,但效果很好。 Since it does use recursion and the potential is there for infinite recursion I did place in a safety against.由于它确实使用了递归,并且存在无限递归的潜力,因此我确实将其置于安全位置。

The below method will ignore the serialVersionUID, iterate through all the fields and copy the non-null values from object a --> object b if they are null in b.下面的方法将忽略 serialVersionUID,遍历所有字段并从对象 a --> 对象 b 复制非空值,如果它们在 b 中为空。 In other words, if any field is null in b, take it from a if there its not null.换句话说,如果 b 中的任何字段为空,则从 a 中取出它,如果它不为空。

public static <T> T combine2Objects(T a, T b) throws InstantiationException,IllegalAccessException{
            T result = (T) a.getClass().newInstance();
            Object[] fields = Arrays.stream(a.getClass().getDeclaredFields()).filter(f -> !f.getName().equals("serialVersionUID")).collect(Collectors.toList()).toArray();
            for (Object fieldobj : fields) {
                Field field = (Field) fieldobj;
                field.set(result, field.get(b) != null ? field.get(b) : field.get(a));
            }
            return result;
    }

try to use class.getFields尝试使用class.getFields

    Field[] fields = YourClass.getFields();
    for (Field field : fields) {
         // get value
         YourObject value = field.get(objectInstance);
         // check the values are different, then update 
         field.set(objetInstance, value);    
    }

try this尝试这个

public <T> T objectMerge(T local, T remote, boolean toappend) throws Exception {
    Class<?> clazz = local.getClass();
    Object merged = clazz.newInstance();
    for (Field field : clazz.getDeclaredFields()) {
        field.setAccessible(true);
        Object localValue = field.get(local);
        Object remoteValue = field.get(remote);
        if (localValue != null) {
            String key = "";
            if (localValue.getClass().getSimpleName().toLowerCase().contains("map")) {
                key = "map";
            } else if (localValue.getClass().getSimpleName().toLowerCase().contains("set")) {
                key = "set";
            } else if (localValue.getClass().getSimpleName().toLowerCase().contains("list")) {
                key = "list";
            } else {
                key = localValue.getClass().getSimpleName();
            }
            switch (key) {
                case "Default":
                case "Detail":
                case "String":
                case "Date":
                case "Integer":
                case "Float":
                case "Long":
                case "Double":
                case "Object":
                    field.set(merged, (remoteValue != null) ? remoteValue : localValue);
                    break;
                case "map":
                    if (toappend) {
                        ((Map) localValue).putAll((Map) remoteValue);
                    } else {
                        localValue = (remoteValue != null) ? remoteValue : localValue;
                    }
                    field.set(merged, localValue);
                    break;
                case "list":
                    if (toappend) {
                        ((List) localValue).addAll((List) remoteValue);
                    } else {
                        localValue = (remoteValue != null) ? remoteValue : localValue;
                    }
                    field.set(merged, localValue);
                    break;
                case "set":
                    if (toappend) {
                        ((Set) localValue).addAll((Set) remoteValue);
                    } else {
                        localValue = (remoteValue != null) ? remoteValue : localValue;
                    }
                    field.set(merged, localValue);
                    break;
                default:
                    field.set(merged, this.objectMerge(localValue, remoteValue, toappend));
                    break;
            }
        }
    }
    return (T) merged;
}
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;

public final class PropertyMerger {

    public static <T> void mergeProperty(
            Supplier<T> sourceGetter,
            Supplier<T> targetGetter,
            Consumer<T> targetSetter
    ) {
        var source = sourceGetter.get();
        var target = targetGetter.get();

        if (!Objects.equals(source, target)) {
            targetSetter.accept(source);
        }
    }

}

Somwhere in your code:在您的代码中的某处:

PropertyMerger.mergeProperty(facebookOAuth2User::getId, existingFacebookOAuth2UserDB::getFacebookId, existingFacebookOAuth2UserDB::setFacebookId);
PropertyMerger.mergeProperty(facebookOAuth2User::getName, existingFacebookOAuth2UserDB::getName, existingFacebookOAuth2UserDB::setName);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM