从一个对象到另一个对象的Java条件映射?

从一个对象到另一个对象的Java条件映射?,java,reflection,spring-rest,Java,Reflection,Spring Rest,希望构建一个API,让客户端指定希望从内部域对象投影到外部域资源的字段 DB-->Foo实体-->Foo映射器-->Foo资源 客户端发送一个名为fieldsToProject e、 g FieldStopProject:[“id”、“名称”、“说明”、“基准价”、“单价”、“制造商”] 我写了一个非常粗糙的方法,但它的工作原理是这样的 public FooResource toProjectedFooResource(Foo foo, List<String> fieldsToPr

希望构建一个API,让客户端指定希望从内部域对象投影到外部域资源的字段

DB-->Foo实体-->Foo映射器-->Foo资源

客户端发送一个名为
fieldsToProject

e、 g

FieldStopProject:[“id”、“名称”、“说明”、“基准价”、“单价”、“制造商”]

我写了一个非常粗糙的方法,但它的工作原理是这样的

public FooResource toProjectedFooResource(Foo foo, List<String> fieldsToProject) {
    FooResource resource = new FooResource();

    if (fieldsToProject.contains("id")) {
        resource.setId(foo.getId());
    }

    if (fieldsToProject.contains("name")) {
        resource.setName(foo.getName());
    }

    if (fieldsToProject.contains("basePrice")) {
        resource.setBasePrice(foo.getBasePrice());
    }

    if (fieldsToProject.contains("unitPrice")) {
        resource.setUnitPrice(foo.getUnitPrice());
    }
    
    //etc.
    return resource;
}
publicFooResourcetoprojectedFooResource(Foo-Foo,List-fieldsToProject){
FooResource资源=新的FooResource();
if(fieldsToProject.contains(“id”)){
setId(foo.getId());
}
if(fieldsToProject.contains(“名称”)){
resource.setName(foo.getName());
}
if(FieldStopProject.contains(“基准价格”)){
resource.setBasePrice(foo.getBasePrice());
}
if(FieldStopProject.contains(“单价”)){
resource.setUnitPrice(foo.getUnitPrice());
}
//等等。
返回资源;
}
有没有一种更简洁或更酷的方法来实现这一点,而不必使用包含所有这些if语句的400行函数

此外,如果客户端发送的字段拼写或大小写不正确,那么解决方案应该忽略它,而不是抛出异常


注意:我使用的是Spring Boot 2.3和Spring Hateoas+Rest,您可以使用反射。我做了一些简单的示例,让您了解它的工作原理:

public class Main {

    public static void main(String[] args) {
        List<String> fieldsToProject = Arrays.asList("test1");
        Test input = new Test();
        input.setTest1("1234");
        input.setTest2("5678");


        Test result = new Test();
        for (String field : fieldsToProject) {
            try {
                //Fields need to be public for this to work
                Field inputField = input.getClass().getField(field);
                Field outputField = result.getClass().getField(field);
                outputField.set(inputField.get(input), result);
            } catch (Exception e) {
                e.printStackTrace();
            }

            try {
                //TODO: Place here some function to change field to camel case
                String fieldCamelCase = "Test1";
                Method inputGetMethod = Arrays.stream(input.getClass().getMethods())
                                       .filter(x -> x.getName().equals("get" + fieldCamelCase))
                                       .findFirst().orElseGet(null);

                Method outputSetMethod = Arrays.stream(input.getClass().getMethods())
                                       .filter(x -> x.getName().equals("set" + fieldCamelCase))
                                       .findFirst().orElseGet(null);

                Object value = inputGetMethod.invoke(input);
                outputSetMethod.invoke(result, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println(result.getTest1());
        System.out.println(result.getTest2());
    }

}



public class Test {
    private String test1;
    private String test2;

    public String getTest1() {
        return test1;
    }

    public Test setTest1(String test1) {
        this.test1 = test1;
        return this;
    }

    public String getTest2() {
        return test2;
    }

    public Test setTest2(String test2) {
        this.test2 = test2;
        return this;
    }
}
公共类主{
公共静态void main(字符串[]args){
List fieldsToProject=Arrays.asList(“test1”);
测试输入=新测试();
输入设置1(“1234”);
输入设置2(“5678”);
测试结果=新测试();
for(字符串字段:fieldsToProject){
试一试{
//字段必须是公共的,才能使其工作
字段inputField=input.getClass().getField(字段);
Field outputField=result.getClass().getField(字段);
outputField.set(inputField.get(input),result);
}捕获(例外e){
e、 printStackTrace();
}
试一试{
//TODO:在此处放置一些函数以将字段更改为camel大小写
字符串fieldCamelCase=“Test1”;
方法inputGetMethod=Arrays.stream(input.getClass().getMethods())
.filter(x->x.getName().equals(“get”+fieldCamelCase))
.findFirst().orelsGet(null);
方法outputSetMethod=Arrays.stream(input.getClass().getMethods())
.filter(x->x.getName().equals(“set”+fieldCamelCase))
.findFirst().orelsGet(null);
对象值=inputGetMethod.invoke(输入);
调用(结果、值);
}捕获(例外e){
e、 printStackTrace();
}
}
System.out.println(result.getTest1());
System.out.println(result.getTest2());
}
}
公开课考试{
私有字符串test1;
私有字符串test2;
公共字符串getTest1(){
返回test1;
}
公共测试集合test1(字符串test1){
this.test1=test1;
归还这个;
}
公共字符串getTest2(){
返回test2;
}
公共测试设置2(字符串测试2){
this.test2=test2;
归还这个;
}
}

它不会涵盖所有案例,但它是一个起点。

您可以使用反射来实现这一点。我做了一些简单的示例,让您了解它的工作原理:

public class Main {

    public static void main(String[] args) {
        List<String> fieldsToProject = Arrays.asList("test1");
        Test input = new Test();
        input.setTest1("1234");
        input.setTest2("5678");


        Test result = new Test();
        for (String field : fieldsToProject) {
            try {
                //Fields need to be public for this to work
                Field inputField = input.getClass().getField(field);
                Field outputField = result.getClass().getField(field);
                outputField.set(inputField.get(input), result);
            } catch (Exception e) {
                e.printStackTrace();
            }

            try {
                //TODO: Place here some function to change field to camel case
                String fieldCamelCase = "Test1";
                Method inputGetMethod = Arrays.stream(input.getClass().getMethods())
                                       .filter(x -> x.getName().equals("get" + fieldCamelCase))
                                       .findFirst().orElseGet(null);

                Method outputSetMethod = Arrays.stream(input.getClass().getMethods())
                                       .filter(x -> x.getName().equals("set" + fieldCamelCase))
                                       .findFirst().orElseGet(null);

                Object value = inputGetMethod.invoke(input);
                outputSetMethod.invoke(result, value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println(result.getTest1());
        System.out.println(result.getTest2());
    }

}



public class Test {
    private String test1;
    private String test2;

    public String getTest1() {
        return test1;
    }

    public Test setTest1(String test1) {
        this.test1 = test1;
        return this;
    }

    public String getTest2() {
        return test2;
    }

    public Test setTest2(String test2) {
        this.test2 = test2;
        return this;
    }
}
公共类主{
公共静态void main(字符串[]args){
List fieldsToProject=Arrays.asList(“test1”);
测试输入=新测试();
输入设置1(“1234”);
输入设置2(“5678”);
测试结果=新测试();
for(字符串字段:fieldsToProject){
试一试{
//字段必须是公共的,才能使其工作
字段inputField=input.getClass().getField(字段);
Field outputField=result.getClass().getField(字段);
outputField.set(inputField.get(input),result);
}捕获(例外e){
e、 printStackTrace();
}
试一试{
//TODO:在此处放置一些函数以将字段更改为camel大小写
字符串fieldCamelCase=“Test1”;
方法inputGetMethod=Arrays.stream(input.getClass().getMethods())
.filter(x->x.getName().equals(“get”+fieldCamelCase))
.findFirst().orelsGet(null);
方法outputSetMethod=Arrays.stream(input.getClass().getMethods())
.filter(x->x.getName().equals(“set”+fieldCamelCase))
.findFirst().orelsGet(null);
对象值=inputGetMethod.invoke(输入);
调用(结果、值);
}捕获(例外e){
e、 printStackTrace();
}
}
System.out.println(result.getTest1());
System.out.println(result.getTest2());
}
}
公开课考试{
私有字符串test1;
私有字符串test2;
公共字符串getTest1(){
返回test1;
}
公共测试集合test1(字符串test1){
this.test1=test1;
归还这个;
}
公共字符串getTest2(){
返回test2;
}
公共测试设置2(字符串测试2){
this.test2=test2;
归还这个;
}
}

它不会涵盖所有情况,但它是一个起点。

使用反射,您确实可以创建更紧凑的代码。我的方法是这样的:

    public FooResource toProjectedFooResource(Foo foo, List<String> fieldsToProject) {
        FooResource fr = new FooResource();
        for (Field field : foo.getClass().getDeclaredFields()) {
            if (fieldsToProject.contains(field.getName())) {
                try {
                    // Notice the use property descriptor for simplicity instead of constructing the getter setter method name by ourselves
                    new PropertyDescriptor(field.getName(), FooResource.class).getWriteMethod().invoke(fr,
                            new PropertyDescriptor(field.getName(), Foo.class).getReadMethod().invoke(foo, (Object[]) null));
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
                        | IntrospectionException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return fr;
    }
publicFooResourcetoprojectedFooResource(Foo-Foo,List-fieldsToProject){
FooResource fr=新的FooResource();