通过反射将一个类中的字段中的所有值复制到另一个类中

时间:2022-09-24 18:47:49

I have a class which is basically a copy of another class.

我有一个类,基本上是另一个类的副本。

public class A {
  int a;
  String b;
}

public class CopyA {
  int a;
  String b;
}

What I am doing is putting values from class A into CopyA before sending CopyA through a webservice call. Now I would like to create a reflection-method that basically copies all fields that are identical (by name and type) from class A to class CopyA.

我所做的是在通过webservice调用发送CopyA之前将A类的值放入CopyA。现在我想创建一个反射-方法,该方法基本上将所有相同的字段(按名称和类型)从类a复制到类CopyA。

How can I do this?

我该怎么做呢?

This is what I have so far, but it doesn't quite work. I think the problem here is that I am trying to set a field on the field I am looping through.

这是我到目前为止所做的,但并不完全有效。我认为这里的问题是我试图在我正在循环的领域设置一个字段。

private <T extends Object, Y extends Object> void copyFields(T from, Y too) {

    Class<? extends Object> fromClass = from.getClass();
    Field[] fromFields = fromClass.getDeclaredFields();

    Class<? extends Object> tooClass = too.getClass();
    Field[] tooFields = tooClass.getDeclaredFields();

    if (fromFields != null && tooFields != null) {
        for (Field tooF : tooFields) {
            logger.debug("toofield name #0 and type #1", tooF.getName(), tooF.getType().toString());
            try {
                // Check if that fields exists in the other method
                Field fromF = fromClass.getDeclaredField(tooF.getName());
                if (fromF.getType().equals(tooF.getType())) {
                    tooF.set(tooF, fromF);
                }
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

I am sure there must be someone that has already done this somehow

我相信一定有人已经做了这件事

15 个解决方案

#1


67  

If you don't mind using a third party library, BeanUtils from Apache Commons will handle this quite easily, using copyProperties(Object, Object).

如果您不介意使用第三方库,那么来自Apache Commons的BeanUtils将使用copyProperties(对象、对象)轻松地处理这个问题。

#2


9  

Why don't you use gson library https://github.com/google/gson

为什么不使用gson库https://github.com/google/gson

you just convert the Class A to json string. Then convert jsonString to you subClass (CopyA) .using below code:

您只需将类A转换为json字符串。然后将jsonString转换为您的子类(CopyA)。

Gson gson= new Gson();
String tmp = gson.toJson(a);
CopyA myObject = gson.fromJson(tmp,CopyA.class);

#3


7  

BeanUtils will only copy public fields and is a bit slow. Instead go with getter and setter methods.

BeanUtils只会复制公共字段,而且有点慢。而是使用getter和setter方法。

public Object loadData (RideHotelsService object_a) throws Exception{

        Method[] gettersAndSetters = object_a.getClass().getMethods();

        for (int i = 0; i < gettersAndSetters.length; i++) {
                String methodName = gettersAndSetters[i].getName();
                try{
                  if(methodName.startsWith("get")){
                     this.getClass().getMethod(methodName.replaceFirst("get", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(object_a, null));
                        }else if(methodName.startsWith("is") ){
                            this.getClass().getMethod(methodName.replaceFirst("is", "set") ,  gettersAndSetters[i].getReturnType()  ).invoke(this, gettersAndSetters[i].invoke(object_a, null));
                        }

                }catch (NoSuchMethodException e) {
                    // TODO: handle exception
                }catch (IllegalArgumentException e) {
                    // TODO: handle exception
                }

        }

        return null;
    }

#4


4  

The first argument to tooF.set() should be the target object (too), not the field, and the second argument should be the value, not the field the value comes from. (To get the value, you need to call fromF.get() -- again passing in a target object, in this case from.)

set()的第一个参数应该是目标对象(也),而不是字段,第二个参数应该是值,而不是值来自的字段。(为了获得值,需要从f .get()调用,再次传入目标对象,在本例中为from。)

Most of the reflection API works this way. You get Field objects, Method objects, and so on from the class, not from an instance, so to use them (except for statics) you generally need to pass them an instance.

大多数反射API都是这样工作的。您可以从类中获取字段对象、方法对象等,而不是从实例中获取,因此要使用它们(除了静态对象),通常需要向它们传递一个实例。

#5


4  

Dozer

推土机

UPDATE Nov 19 2012: There's now a new ModelMapper project too.

2012年11月19日更新:现在也有一个新的ModelMapper项目。

#6


4  

  1. Without using BeanUtils or Apache Commons

    不使用BeanUtils或Apache Commons

  2. public static <T1 extends Object, T2 extends Object>  void copy(T1     
    origEntity, T2 destEntity) throws IllegalAccessException, NoSuchFieldException {
        Field[] fields = origEntity.getClass().getDeclaredFields();
        for (Field field : fields){
            origFields.set(destEntity, field.get(origEntity));
         }
    }
    
  3. 公共静态 void copy(T1)。 扩展对象,t2扩展对象>

#7


3  

I think you can try dozer. It has good support for bean to bean conversion. Its also easy to use. You can either inject it into your spring application or add the jar in class path and its done.

我想你可以试试dozer。它对bean到bean的转换有很好的支持。它也很容易使用。您可以将它注入到spring应用程序中,也可以将jar添加到类路径中并完成它。

For an example of your case :

举一个例子:

 DozerMapper mapper = new DozerMapper();
A a= new A();
CopyA copyA = new CopyA();
a.set... // set fields of a.
mapper.map(a,copyOfA); // will copy all fields from a to copyA

#8


3  

My solution:

我的解决方案:

public static <T > void copyAllFields(T to, T from) {
        Class<T> clazz = (Class<T>) from.getClass();
        // OR:
        // Class<T> clazz = (Class<T>) to.getClass();
        List<Field> fields = getAllModelFields(clazz);

        if (fields != null) {
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    field.set(to,field.get(from));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}

#9


2  

Here is a working and tested solution. You can control the depth of the mapping in the class hierarchy.

这是一个有效且经过测试的解决方案。您可以控制类层次结构中映射的深度。

public class FieldMapper {

    public static void copy(Object from, Object to) throws Exception {
        FieldMapper.copy(from, to, Object.class);
    }

    public static void copy(Object from, Object to, Class depth) throws Exception {
        Class fromClass = from.getClass();
        Class toClass = to.getClass();
        List<Field> fromFields = collectFields(fromClass, depth);
        List<Field> toFields = collectFields(toClass, depth);
        Field target;
        for (Field source : fromFields) {
            if ((target = findAndRemove(source, toFields)) != null) {
                target.set(to, source.get(from));
            }
        }
    }

    private static List<Field> collectFields(Class c, Class depth) {
        List<Field> accessibleFields = new ArrayList<>();
        do {
            int modifiers;
            for (Field field : c.getDeclaredFields()) {
                modifiers = field.getModifiers();
                if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
                    accessibleFields.add(field);
                }
            }
            c = c.getSuperclass();
        } while (c != null && c != depth);
        return accessibleFields;
    }

    private static Field findAndRemove(Field field, List<Field> fields) {
        Field actual;
        for (Iterator<Field> i = fields.iterator(); i.hasNext();) {
            actual = i.next();
            if (field.getName().equals(actual.getName())
                && field.getType().equals(actual.getType())) {
                i.remove();
                return actual;
            }
        }
        return null;
    }
}

#10


1  

Yeah or the BeanUtils from Apache Jakarta.

是的,或者是来自阿帕奇雅加达的BeanUtils。

#11


1  

Orika's is simple faster bean mapping framework because it does through byte code generation. It does nested mappings and mappings with different names. For more details, please check here Sample mapping may look complex, but for complex scenarios it would be simple.

Orika's是一个简单的更快的bean映射框架,因为它是通过字节代码生成的。它执行不同名称的嵌套映射和映射。要了解更多细节,请检查这里的示例映射可能看起来很复杂,但是对于复杂的场景,它可能很简单。

MapperFactory factory = new DefaultMapperFactory.Builder().build();
mapperFactory.registerClassMap(mapperFactory.classMap(Book.class,BookDto.class).byDefault().toClassMap());
MapperFacade mapper = factory.getMapperFacade();
BookDto bookDto = mapperFacade.map(book, BookDto.class);

#12


1  

If you have spring in the dependencies you can also use org.springframework.beans.BeanUtils.

如果依赖项中有spring,还可以使用org.springframe . beans.beanutils。

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

#13


1  

Spring has a built in BeanUtils.copyProperties method. But it doesn't work with classes without getters/setters. JSON serialization/deserialization can be another option for copying fields. Jackson can be used for this purpose. If you are using Spring In most cases Jackson is already in your dependency list.

春天有锦绣锦绣锦绣。copyProperties方法。但是没有getter /setter就不能使用它。JSON序列化/反序列化是复制字段的另一个选项。杰克逊可以被用于这个目的。如果您正在使用Spring,在大多数情况下,Jackson已经在您的依赖列表中。

ObjectMapper mapper     = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Clazz        copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);

#14


1  

This is a late post, but can still be effective for people in future.

这是一个较晚的帖子,但仍然可以有效的为人们在未来。

Spring provides a utility BeanUtils.copyProperties(srcObj, tarObj) which copies values from source object to target object when the names of the member variables of both classes are the same.

Spring提供了一种实用的BeanUtils。copyProperties(srcObj, tarObj),当两个类的成员变量的名称相同时,它将值从源对象复制到目标对象。

If there is a date conversion, (eg, String to Date) 'null' would be copied to the target object. We can then, explicitly set the values of the date as required.

如果有日期转换,(例如,到日期的字符串)'null'将被复制到目标对象。然后,我们可以根据需要显式地设置日期的值。

The BeanUtils from Apache Common throws an error when there is a mismatch of data-types (esp. conversion to and from Date)

Apache Common中的BeanUtils会在数据类型不匹配时抛出错误(尤其是数据类型与日期之间的转换)。

Hope this helps!

希望这可以帮助!

#15


0  

I didn't want to add dependency to another JAR file because of this, so wrote something which would suit my needs. I follow the convention of fjorm https://code.google.com/p/fjorm/ which means that my generally accessible fields are public and that I don't bother to write setters and getters. (in my opinion code is easier to manage and more readable actually)

因为这个原因,我不想向另一个JAR文件添加依赖项,所以编写了一些适合我需要的东西。我遵循fjorm https://code.google.com/p/fjorm/的约定,这意味着我的一般可访问字段是公共的,我不必费心编写setter和getter。(在我看来,代码更容易管理,更容易阅读)

So I wrote something (it's not actually much difficult) which suits my needs (assumes that the class has public constructor without args) and it could be extracted into utility class

因此,我编写了一些适合我需要的东西(实际上并不难)(假设类有没有args的公共构造函数),并且可以将其提取到实用程序类中

  public Effect copyUsingReflection() {
    Constructor constructorToUse = null;
    for (Constructor constructor : this.getClass().getConstructors()) {
      if (constructor.getParameterTypes().length == 0) {
        constructorToUse = constructor;
        constructorToUse.setAccessible(true);
      }
    }
    if (constructorToUse != null) {
      try {
        Effect copyOfEffect = (Effect) constructorToUse.newInstance();
        for (Field field : this.getClass().getFields()) {
          try {
            Object valueToCopy = field.get(this);
            //if it has field of the same type (Effect in this case), call the method to copy it recursively
            if (valueToCopy instanceof Effect) {
              valueToCopy = ((Effect) valueToCopy).copyUsingReflection();
            }
            //TODO add here other special types of fields, like Maps, Lists, etc.
            field.set(copyOfEffect, valueToCopy);
          } catch (IllegalArgumentException | IllegalAccessException ex) {
            Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
        return copyOfEffect;
      } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
    return null;
  }

#1


67  

If you don't mind using a third party library, BeanUtils from Apache Commons will handle this quite easily, using copyProperties(Object, Object).

如果您不介意使用第三方库,那么来自Apache Commons的BeanUtils将使用copyProperties(对象、对象)轻松地处理这个问题。

#2


9  

Why don't you use gson library https://github.com/google/gson

为什么不使用gson库https://github.com/google/gson

you just convert the Class A to json string. Then convert jsonString to you subClass (CopyA) .using below code:

您只需将类A转换为json字符串。然后将jsonString转换为您的子类(CopyA)。

Gson gson= new Gson();
String tmp = gson.toJson(a);
CopyA myObject = gson.fromJson(tmp,CopyA.class);

#3


7  

BeanUtils will only copy public fields and is a bit slow. Instead go with getter and setter methods.

BeanUtils只会复制公共字段,而且有点慢。而是使用getter和setter方法。

public Object loadData (RideHotelsService object_a) throws Exception{

        Method[] gettersAndSetters = object_a.getClass().getMethods();

        for (int i = 0; i < gettersAndSetters.length; i++) {
                String methodName = gettersAndSetters[i].getName();
                try{
                  if(methodName.startsWith("get")){
                     this.getClass().getMethod(methodName.replaceFirst("get", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(object_a, null));
                        }else if(methodName.startsWith("is") ){
                            this.getClass().getMethod(methodName.replaceFirst("is", "set") ,  gettersAndSetters[i].getReturnType()  ).invoke(this, gettersAndSetters[i].invoke(object_a, null));
                        }

                }catch (NoSuchMethodException e) {
                    // TODO: handle exception
                }catch (IllegalArgumentException e) {
                    // TODO: handle exception
                }

        }

        return null;
    }

#4


4  

The first argument to tooF.set() should be the target object (too), not the field, and the second argument should be the value, not the field the value comes from. (To get the value, you need to call fromF.get() -- again passing in a target object, in this case from.)

set()的第一个参数应该是目标对象(也),而不是字段,第二个参数应该是值,而不是值来自的字段。(为了获得值,需要从f .get()调用,再次传入目标对象,在本例中为from。)

Most of the reflection API works this way. You get Field objects, Method objects, and so on from the class, not from an instance, so to use them (except for statics) you generally need to pass them an instance.

大多数反射API都是这样工作的。您可以从类中获取字段对象、方法对象等,而不是从实例中获取,因此要使用它们(除了静态对象),通常需要向它们传递一个实例。

#5


4  

Dozer

推土机

UPDATE Nov 19 2012: There's now a new ModelMapper project too.

2012年11月19日更新:现在也有一个新的ModelMapper项目。

#6


4  

  1. Without using BeanUtils or Apache Commons

    不使用BeanUtils或Apache Commons

  2. public static <T1 extends Object, T2 extends Object>  void copy(T1     
    origEntity, T2 destEntity) throws IllegalAccessException, NoSuchFieldException {
        Field[] fields = origEntity.getClass().getDeclaredFields();
        for (Field field : fields){
            origFields.set(destEntity, field.get(origEntity));
         }
    }
    
  3. 公共静态 void copy(T1)。 扩展对象,t2扩展对象>

#7


3  

I think you can try dozer. It has good support for bean to bean conversion. Its also easy to use. You can either inject it into your spring application or add the jar in class path and its done.

我想你可以试试dozer。它对bean到bean的转换有很好的支持。它也很容易使用。您可以将它注入到spring应用程序中,也可以将jar添加到类路径中并完成它。

For an example of your case :

举一个例子:

 DozerMapper mapper = new DozerMapper();
A a= new A();
CopyA copyA = new CopyA();
a.set... // set fields of a.
mapper.map(a,copyOfA); // will copy all fields from a to copyA

#8


3  

My solution:

我的解决方案:

public static <T > void copyAllFields(T to, T from) {
        Class<T> clazz = (Class<T>) from.getClass();
        // OR:
        // Class<T> clazz = (Class<T>) to.getClass();
        List<Field> fields = getAllModelFields(clazz);

        if (fields != null) {
            for (Field field : fields) {
                try {
                    field.setAccessible(true);
                    field.set(to,field.get(from));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

public static List<Field> getAllModelFields(Class aClass) {
    List<Field> fields = new ArrayList<>();
    do {
        Collections.addAll(fields, aClass.getDeclaredFields());
        aClass = aClass.getSuperclass();
    } while (aClass != null);
    return fields;
}

#9


2  

Here is a working and tested solution. You can control the depth of the mapping in the class hierarchy.

这是一个有效且经过测试的解决方案。您可以控制类层次结构中映射的深度。

public class FieldMapper {

    public static void copy(Object from, Object to) throws Exception {
        FieldMapper.copy(from, to, Object.class);
    }

    public static void copy(Object from, Object to, Class depth) throws Exception {
        Class fromClass = from.getClass();
        Class toClass = to.getClass();
        List<Field> fromFields = collectFields(fromClass, depth);
        List<Field> toFields = collectFields(toClass, depth);
        Field target;
        for (Field source : fromFields) {
            if ((target = findAndRemove(source, toFields)) != null) {
                target.set(to, source.get(from));
            }
        }
    }

    private static List<Field> collectFields(Class c, Class depth) {
        List<Field> accessibleFields = new ArrayList<>();
        do {
            int modifiers;
            for (Field field : c.getDeclaredFields()) {
                modifiers = field.getModifiers();
                if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
                    accessibleFields.add(field);
                }
            }
            c = c.getSuperclass();
        } while (c != null && c != depth);
        return accessibleFields;
    }

    private static Field findAndRemove(Field field, List<Field> fields) {
        Field actual;
        for (Iterator<Field> i = fields.iterator(); i.hasNext();) {
            actual = i.next();
            if (field.getName().equals(actual.getName())
                && field.getType().equals(actual.getType())) {
                i.remove();
                return actual;
            }
        }
        return null;
    }
}

#10


1  

Yeah or the BeanUtils from Apache Jakarta.

是的,或者是来自阿帕奇雅加达的BeanUtils。

#11


1  

Orika's is simple faster bean mapping framework because it does through byte code generation. It does nested mappings and mappings with different names. For more details, please check here Sample mapping may look complex, but for complex scenarios it would be simple.

Orika's是一个简单的更快的bean映射框架,因为它是通过字节代码生成的。它执行不同名称的嵌套映射和映射。要了解更多细节,请检查这里的示例映射可能看起来很复杂,但是对于复杂的场景,它可能很简单。

MapperFactory factory = new DefaultMapperFactory.Builder().build();
mapperFactory.registerClassMap(mapperFactory.classMap(Book.class,BookDto.class).byDefault().toClassMap());
MapperFacade mapper = factory.getMapperFacade();
BookDto bookDto = mapperFacade.map(book, BookDto.class);

#12


1  

If you have spring in the dependencies you can also use org.springframework.beans.BeanUtils.

如果依赖项中有spring,还可以使用org.springframe . beans.beanutils。

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

#13


1  

Spring has a built in BeanUtils.copyProperties method. But it doesn't work with classes without getters/setters. JSON serialization/deserialization can be another option for copying fields. Jackson can be used for this purpose. If you are using Spring In most cases Jackson is already in your dependency list.

春天有锦绣锦绣锦绣。copyProperties方法。但是没有getter /setter就不能使用它。JSON序列化/反序列化是复制字段的另一个选项。杰克逊可以被用于这个目的。如果您正在使用Spring,在大多数情况下,Jackson已经在您的依赖列表中。

ObjectMapper mapper     = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Clazz        copyObject = mapper.readValue(mapper.writeValueAsString(sourceObject), Clazz.class);

#14


1  

This is a late post, but can still be effective for people in future.

这是一个较晚的帖子,但仍然可以有效的为人们在未来。

Spring provides a utility BeanUtils.copyProperties(srcObj, tarObj) which copies values from source object to target object when the names of the member variables of both classes are the same.

Spring提供了一种实用的BeanUtils。copyProperties(srcObj, tarObj),当两个类的成员变量的名称相同时,它将值从源对象复制到目标对象。

If there is a date conversion, (eg, String to Date) 'null' would be copied to the target object. We can then, explicitly set the values of the date as required.

如果有日期转换,(例如,到日期的字符串)'null'将被复制到目标对象。然后,我们可以根据需要显式地设置日期的值。

The BeanUtils from Apache Common throws an error when there is a mismatch of data-types (esp. conversion to and from Date)

Apache Common中的BeanUtils会在数据类型不匹配时抛出错误(尤其是数据类型与日期之间的转换)。

Hope this helps!

希望这可以帮助!

#15


0  

I didn't want to add dependency to another JAR file because of this, so wrote something which would suit my needs. I follow the convention of fjorm https://code.google.com/p/fjorm/ which means that my generally accessible fields are public and that I don't bother to write setters and getters. (in my opinion code is easier to manage and more readable actually)

因为这个原因,我不想向另一个JAR文件添加依赖项,所以编写了一些适合我需要的东西。我遵循fjorm https://code.google.com/p/fjorm/的约定,这意味着我的一般可访问字段是公共的,我不必费心编写setter和getter。(在我看来,代码更容易管理,更容易阅读)

So I wrote something (it's not actually much difficult) which suits my needs (assumes that the class has public constructor without args) and it could be extracted into utility class

因此,我编写了一些适合我需要的东西(实际上并不难)(假设类有没有args的公共构造函数),并且可以将其提取到实用程序类中

  public Effect copyUsingReflection() {
    Constructor constructorToUse = null;
    for (Constructor constructor : this.getClass().getConstructors()) {
      if (constructor.getParameterTypes().length == 0) {
        constructorToUse = constructor;
        constructorToUse.setAccessible(true);
      }
    }
    if (constructorToUse != null) {
      try {
        Effect copyOfEffect = (Effect) constructorToUse.newInstance();
        for (Field field : this.getClass().getFields()) {
          try {
            Object valueToCopy = field.get(this);
            //if it has field of the same type (Effect in this case), call the method to copy it recursively
            if (valueToCopy instanceof Effect) {
              valueToCopy = ((Effect) valueToCopy).copyUsingReflection();
            }
            //TODO add here other special types of fields, like Maps, Lists, etc.
            field.set(copyOfEffect, valueToCopy);
          } catch (IllegalArgumentException | IllegalAccessException ex) {
            Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
        return copyOfEffect;
      } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        Logger.getLogger(Effect.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
    return null;
  }