Sure, here’s the translation: 基于点注释的Java嵌套POJO更新

huangapple go评论104阅读模式
英文:

Java nested POJO update based on dot annotation

问题

我有一个类似这样定义的嵌套POJO结构,

public class Employee {
    private String id;
    private Personal personal;
    private Official official;
}

public class Personal {
    private String fName;
    private String lName;
    private String address;
}

public class Official {
    private boolean active;
    private Salary salary;
}

public class Salary {
    private double hourly;
    private double monthly;
    private double yearly;
}

我从一个服务中获取带有点注释的更新,表示哪个值发生了变化,例如,

id更改 --> id=100
地址更改 --> personal.address=123 Main Street 
每小时工资更改 --> official.salary.hourly=100

这个POJO结构可能会有3-4层的深度。我需要查找这个传入的变更值,并更新POJO中相应的值。最好的做法是什么?

英文:

I have a nested POJO structure defined something like this,

public class Employee {
	private String id;
	private Personal personal;
	private Official official;
}

public class Personal {
	private String fName;
	private String lName;
	private String address;
}

public class Official {
	private boolean active;
	private Salary salary;
}

public class Salary {
	private double hourly;
	private double monthly;
	private double yearly;
}

I get updates from a service with dot annotaion on what value changed, for ex,

id change --> id=100
address change --> personal.address=123 Main Street 
hourly salary change --> official.salary.hourly=100

This POJO structure could be 3-4 level deeps. I need to look for this incoming change value and update the corresponding value in POJO. What's the best way of doing it?

答案1

得分: 1

这是一种使用反射动态设置字段的快速方法。这确实不是一个干净的方法,如果我是你,我会使用脚本引擎来实现这个(假设这样做是安全的)。

private static void setValueAt(Object target, String path, String value) 
        throws Exception {
    
    String[] fields = path.split("\\.");
    if (fields.length > 1) {
        setValueAt(readField(target, fields[0]), 
                path.substring(path.indexOf('.') + 1), value);
        return;
    }

    Field f = target.getClass()
            .getDeclaredField(path);
    f.setAccessible(true);
    f.set(target, parse(value, f.getType())); // 先转换或转换值
}

// 将字符串转换为基本类型的示例代码
private static Object parse(String value, Class<?> type) {
    if (String.class.equals(type)) {
        return value;
    } else if (double.class.equals(type) || Double.class.equals(type)) {
        return Double.parseDouble(value);
    } else if (boolean.class.equals(type) || Boolean.class.equals(type)) {
        return Boolean.valueOf(value);
    }
    return value;// ?
}

private static Object readField(Object from, String field) throws Exception {
    Field f = from.getClass()
            .getDeclaredField(field);
    f.setAccessible(true);
    return f.get(from);
}

请注意,这段代码还有很多需要改进的地方(异常处理、空值检查等),尽管它似乎已经实现了你要的功能(将输入拆分为 = 并调用 setValueAt()):

Employee e = new Employee();
e.setOfficial(new Official());
e.setPersonal(new Personal());
e.getOfficial().setSalary(new Salary());

ObjectMapper mapper = new ObjectMapper();
setValueAt(e, "id", "123");
// {"id":"123","personal":{},"official":{"active":false,"salary":{"hourly":0.0,"monthly":0.0,"yearly":0.0}}}

setValueAt(e, "personal.address", "123 Main Street");
// {"id":"123","personal":{"address":"123 Main Street"},"official":{"active":false,"salary":{"hourly":0.0,"monthly":0.0,"yearly":0.0}}}

setValueAt(e, "official.salary.hourly", "100");
// {"id":"123","personal":{"address":"123 Main Street"},"official":{"active":false,"salary":{"hourly":100.0,"monthly":0.0,"yearly":0.0}}}
英文:

Here's a quick approach using reflection to set fields dynamically. It surely isn't and can't be clean. If I were you, I would use a scripting engine for that (assuming it's safe to do so).

private static void setValueAt(Object target, String path, String value) 
throws Exception {
String[] fields = path.split(&quot;\\.&quot;);
if (fields.length &gt; 1) {
setValueAt(readField(target, fields[0]), 
path.substring(path.indexOf(&#39;.&#39;) + 1), value);
return;
}
Field f = target.getClass()
.getDeclaredField(path);
f.setAccessible(true);
f.set(target, parse(value, f.getType())); // cast or convert value first
}
//Example code for converting strings to primitives
private static Object parse(String value, Class&lt;?&gt; type) {
if (String.class.equals(type)) {
return value;
} else if (double.class.equals(type) || Double.class.equals(type)) {
return Long.parseLong(value);
} else if (boolean.class.equals(type) || Boolean.class.equals(type)) {
return Boolean.valueOf(value);
}
return value;// ?
}
private static Object readField(Object from, String field) throws Exception {
Field f = from.getClass()
.getDeclaredField(field);
f.setAccessible(true);
return f.get(from);
}

Just be aware that there's a lot to improve in this code (exception handling, null checks, etc.), although it seems to achieve what you're looking for (split your input on = to call setValueAt()):

Employee e = new Employee();
e.setOfficial(new Official());
e.setPersonal(new Personal());
e.getOfficial().setSalary(new Salary());
ObjectMapper mapper = new ObjectMapper();
setValueAt(e, &quot;id&quot;, &quot;123&quot;);
// {&quot;id&quot;:&quot;123&quot;,&quot;personal&quot;:{},&quot;official&quot;:{&quot;active&quot;:false,&quot;salary&quot;:{&quot;hourly&quot;:0.0,&quot;monthly&quot;:0.0,&quot;yearly&quot;:0.0}}}
setValueAt(e, &quot;personal.address&quot;, &quot;123 Main Street&quot;);
// {&quot;id&quot;:&quot;123&quot;,&quot;personal&quot;:{&quot;address&quot;:&quot;123 Main Street&quot;},&quot;official&quot;:{&quot;active&quot;:false,&quot;salary&quot;:{&quot;hourly&quot;:0.0,&quot;monthly&quot;:0.0,&quot;yearly&quot;:0.0}}}
setValueAt(e, &quot;official.salary.hourly&quot;, &quot;100&quot;);
// {&quot;id&quot;:&quot;123&quot;,&quot;personal&quot;:{&quot;address&quot;:&quot;123 Main Street&quot;},&quot;official&quot;:{&quot;active&quot;:false,&quot;salary&quot;:{&quot;hourly&quot;:100.0,&quot;monthly&quot;:0.0,&quot;yearly&quot;:0.0}}}

答案2

得分: 0

如果您想创建允许您编辑字段的Java对象,您可以使用public/default/protected访问修饰符指定对象字段。这将使您能够获取和设置诸如personal.addressofficial.salary.hours之类的字段。

这种方法通常不受欢迎,因为对象不再封装,并且任何调用方法都可以操纵该对象。如果没有使用getter和setter封装这些字段,您的对象将不再是一个“POJO”。

public允许从任何地方访问。

default允许从任何包访问。

protected允许从包或子类访问。

public class Employee {
    public String id;
    public Personal personal;
    public Official official;
}

public class Personal {
    public String fName;
    public String lName;
    public String address;
}
英文:

If you would like to create Java objects that allows you to edit fields. You can specify your object fields with the public/default/protected access modifiers. This will enable you to get and set fields such as personal.address or official.salary.hours

This approach is typically frowned upon as the object is no longer encapsulated and any calling methods are welcome to manipulate the object. If these fields are not encapsulated with getters and setters, your object is no longer a POJO.

public provides access from any anywhere.

default provides access from any package

protected provides access from package or subclass.

public class Employee {
public String id;
public Personal personal;
public Official official;
}
public class Personal {
public String fName;
public String lName;
public String address;
}

huangapple
  • 本文由 发表于 2020年8月29日 13:05:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/63643628.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定