Get the field name from getter method - java

I need to capture field name of getter method dynamically for dynamic validation and dynamic formatting.
What is the best and efficient way of doing this.
public class Emp{
private String firstName;
private String lastName;
private String address;
private int age;
// getter and setters
}
public class MyImplementationClass{
public execute(Emp emp){
String fName=emp.getFirstName();
// field name need to be taken here using 'emp.getFirstName()'
// need field name and value of return value of 'emp.getFirstName()' for dynamic validation and dynamic formatting.
// here need to call method validateAndFormat() with field name and value.
}
}
private String validateAndFormat(String fieldName,String value){
// read the dynamic validation settings from the xml and validate/reformat the value
// this method will validate the field according to xml and return reformatted value.
}
private int validateAndFormat(String fieldName,int value){
//...
}
dynamic validation settings
<message>
<element field="firstName" length="22" defaultVal=""></element>
<element field="lastName" length="20" defaultVal="ibft"></element>
<element field="address" length="NA" defaultVal=""></element>
<element field="age" length="NA" defaultVal=""></element>
</message>

use getMethods get all public methods of the emp
then choose getXXX method and invoke it
Emp emp = new Emp();
emp.setAddress("myAdress");
emp.setAge(20);
emp.setFirstName("myFirstName");
emp.setLastName("myLastName");
Class clz = emp.getClass();
Method[] methods = clz.getMethods();
for (Method method : methods) {
String methodName = method.getName();
if (!Objects.equals(methodName, "getClass")
&& methodName.startsWith("get")
&& methodName.length() > 3
&& method.getParameterTypes().length == 0) {
String field = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
Object value = method.invoke(emp);
System.out.println("field:" + field + ",value:" + value);
}
}
you can also use getDeclaredFields get all private fields
and find getXXX method by field
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
try {
Method method = clz.getMethod(methodName);
Object value = method.invoke(emp);
System.out.println("field:" + fieldName + ",value:" + value);
} catch (NoSuchMethodException e) {
System.out.println("no such method:" + methodName);
}
}

You can use Java reflection. But getting the field name from getter might not be a good design as the getter may not be backed by a field. Consider the below getter.
public int getExp(){
return today-joiningDate();
}
This is not backed by a field.
Instead, if you want only the field name and value, you can achieve as below.
Class class1 = employee.getClass();
Field[] fields= class1.getDeclaredFields();
for(Field field:fields){
try {
field.setAccessible(true);
System.out.println(field.getName()+":"+field.get(bean));
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
Hope this helps.

annotation would be a choose.
public #interface MyField {
String fieldName() default "";
}
and use it on you method
#MyField("firstName")
getFirstName(){
...
}
then get fieldName by reflection. i think this way is more flexible than generate field name from method name.

Related

How to copy values of all attributes from one instance of an object to another if they are not null or empty

Given a class Person:
String name;
String surname;
String id;
String address;
I have an object obj1 with the following values:
name="Name"
surname=null
id="ABC123"
address="Here"
Through an api call I get the following json:
{
"name":"John",
"surname":"Doe",
"id":"A1B2C3"
}
which gets mapped into an object obj2like this:
name="John"
surname="Doe"
id="A1B2C3"
address=null
I want to copy all non-null (or empty string) values of obj2 into obj1 so the final result is this:
name="John"
surname="Doe"
id="A1B2C3"
address="Here"
I have two problems.
The first one is that I don't want to have to manually type the get/set call for each attribute of the object.
The second problem, is that I want the method to work for any type of object with no or minimal changes.
At the very least, I need the first problem solved. The second one is optional, but would be great to learn a solution too.
You can use reflection to get all the instance fields and use Field#set to copy over non-null values.
try {
for (Field field : Person.class.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
Object val = field.get(obj2);
if (val != null) {
field.set(obj1, val);
}
}
}
System.out.println(obj1);
} catch (IllegalAccessException e) {
// Handle exception
}
Demo
public static void main(String[] args) {
MyClass obj1 = new MyClass(1,"1");
MyClass obj2 = new MyClass(2,"2");
System.out.println(obj2);
copy(obj1, obj2);
System.out.println(obj2);
}
public static MyClass copy(MyClass obj1, MyClass obj2){
Arrays.stream(obj1.getClass().getDeclaredFields()).forEach(f -> {
try {
f.set(obj2, f.get(obj1));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return obj2;
}
static class MyClass {
int myInt;
String myString;
String emptyString = null;
public MyClass(int myInt, String myString) {
this.myInt = myInt;
this.myString = myString;
}
#Override
public String toString() {
return "MyClass{" +
"myInt=" + myInt +
", myString='" + myString + '\'' +
", emptyString='" + emptyString + '\'' +
'}';
}
}
in case you have private fields you can use
f.setAccessible(true);
but I don't suggest that.
Reading your question, seems like, you have two classes, in two distinct projects, which, exchange information in json.
So, It is easy if you can use Jackson(default in spring)
You could annotate the target class with the annotation of jackson #JsonInclude(JsonInclude.Include.NON_NULL)

Identify the property by passing #JsonProperty value to object mapper

I am using spring and hibernate. I have a class (DTO) with a lot of string member variables. I'm trying to implement search for this class. The user should be able to search by each field. I'm using jackson json mapper to serialize and deserialize objects. Is there anyway to identify the fieldName by using JsonProperty value?
Let this be an example: my DTO
public class SampleDTO{
private String field1;
private String field2;
private String field3;
private String field4;
#JsonProperty("FIELD_1")
public String getField1(){
return field1;
}
#JsonProperty("FIELD_2")
public String getField2(){
return field2;
}
#JsonProperty("FIELD_3")
public String getField3(){
return field3;
}
#JsonProperty("FIELD_4")
public String getField4(){
return field4;
}
}
Let this be my search function
public Set<T> search(String fieldName, String searchKeyword) {
String originalFieldName = someMagicFunction(fieldName);
//if fieldName= "FIELD_1", someMagicFunction should return "field1"
Criteria criteria = session.createCriteria(T.class);
criteria.add(Restrictions.eq(originalFieldName, searchKeyword));
return new HashSet<T>(criteria.list());
}
Any implementation is fine. I'm looking for a good approach to handle cases like this. It feels like finding fields manually involves "too much typing".
You basically want to use reflection. There are two possibilities here when it comes to field lookup:
Value of #JsonProperty annotation
Real name of the field
In the first case you may want to use some additional library to ease the pain when using reflection + annotation, but the crude code would look more less like this:
SampleDTO dto = new SampleDTO();
// setup some values here
Field[] fields = r.getClass().getFields();
for(Field f : fields) {
JsonProperty jsonProperty = f.getDeclaredAnnotation(JsonProperty.class);
if (jsonProperty != null && jsonProperty.value().equals("FIELD_1")) {
return (String) f.get(dto);
}
// throw exception since passed field name is illegal
}
In the second one it would be so much easier:
SampleDTO dto = new SampleDTO();
// setup some values here
String field1Value = (String) r.getClass().getField("field1").get(dto);
In case if anyone is interested, this is how I solved the problem. I added this code to DAO's constructor.
try {
BeanInfo beanInfo = Introspector.getBeanInfo(T.class);
Method[] methods = T.class.getMethods();
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor propertyDescriptor: propertyDescriptors) {
//I'm looking for string fields only
if (propertyDescriptor.getPropertyType().equals( String.class)) {
//My annotations are on methods
for(Method method: methods) {
if(propertyDescriptor.getReadMethod().equals(method)) {
JsonProperty jsonProperty = method.getAnnotation(JsonProperty.class);
if (jsonProperty != null) {
//jsonFieldMapping is a Map<String,String>
//will be saving the mapping in the format {"FIELD_1":"field1", "FIELD_2":"field2"}
jsonFieldMapping.put(jsonProperty.value(), propertyDescriptor.getDisplayName());
} else {
logger.debug("jsonProperty is null");
}
}
}
}
}
// just printing out the values identified from class
for(String key: jsonFieldMapping.keySet()) {
logger.debug("key: " + key + "value: " + jsonFieldMapping.get(key));
}
} catch (IntrospectionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
So, my magic method will be
public String getField(String jsonFieldName){
if (jsonFieldMapping.containsKey(jsonFieldName)) {
return jsonFieldMapping.get(jsonFieldName);
} else {
throw new IllegalArgumentException("searching field not found");
}
}
I haven't tested this code completely. Looks like the values in the logs are correct.

Set value to a class variable whose name matches to a string data

Scenario
I have a list of strings which resembles the names of class variables of a a class. So now i have to set values to those variables using the list.
Example
class Abc {
private String name;
private String Id;
private String sal;
}
public static void main(String args[]){
Map<String,String> variablesMap = new HashMap<String,String>();
variablesMap.add("name","Joshi");
variablesMap.add("id","101");
/*Here i want to write the code to set the values for those variables*/
}
Tried
Class ABC contains all getters and setters.
Using java.lang.Field class i can get the list of variables by ABC.class.getDeclaredFileds(). But after that how do i set the values for those.
You can get a Field for your given name via Field field = getClass().getDeclaredField(name);
Then have a look at the Java Docs: Field Documentation
There you have multiple options to set the value for your variable according to the data type.
Have a look at this example: EDIT: updated to suit your exact question
import java.lang.reflect.Field;
import java.util.Map;
import java.util.HashMap;
public class FieldTest {
String name;
String id;
public static void main(String[] args) {
new FieldTest();
}
public FieldTest() {
Map<String,String> variablesMap = new HashMap<String,String>();
variablesMap.put("name","Joshi");
variablesMap.put("id","101");
for (Map.Entry<String, String> entry : variablesMap.entrySet())
{
try {
test(entry.getKey(),entry.getValue());
}
catch(NoSuchFieldException ex) {
ex.printStackTrace();
}
catch(IllegalAccessException e) {
e.printStackTrace();
}
}
System.out.println(name);
System.out.println(id);
}
private void test(String name, String value) throws NoSuchFieldException, IllegalAccessException {
Field field = getClass().getDeclaredField(name);
field.set(this,value);
}
}
You need to use reflection for this.
say for variable 'name' you have a method getName() in class ABC.
so,
create a method name by iterating your variables map.
then get a method name in string,
String nameMethod = "getName";
Object obj=null;
try
{
method = getClass().getMethod(nameMethod);
obj= method.invoke(this);//if blank then method return null for integer and date column
}
catch(NoSuchMethodException e) {
}
you can not use this snippet as it is, you need to make some modifications
You have to tell the Field which instance to set the values of:
ABC abc = new ABC();
for (Map.Entry<String, String> entry : variablesMap.entrySet())
ABC.class.getDeclaredField(entry.getKey()).set(abc, entry.getValue());
You need to use reflection to set the value of your fields.
One option is to use the set() method of a Field:
getClass().getDeclaredField(myFieldName).set(myObject, myValue);
If you want to use the setter method to set the value, you need to generate the name of the method. And set the value using invoke():
String methodName = "set" + myFieldName.subString(0, 1).toUpperCase() + myFieldName.subString(1);
Method method = getClass().getMethod(methodName, String.class);
method.invoke(myObject, myValue);
//abc is instance of class ABC
BeanInfo info = Introspector.getBeanInfo(abc.getClass(),Object.class);
PropertyDescriptor[] props1 = info.getPropertyDescriptors();
for (PropertyDescriptor pd : props1) {
String name = pd.getName();
String arg1 = variablesMap.get(name);
Method setter = pd.getWriteMethod();
setter.invoke(abc, arg1);
}

Get fields and their values at runtime using Java Reflection

I am trying to get fields and their values of an object at runtime. Below is the code sample:
public static int calculateProfileStrenght(Object inputObj,
Map<String, Integer> configMap) throws IllegalArgumentException,
IllegalAccessException {
int someValue= 0;
for (Entry<String, Integer> entry : configMap.entrySet()) {
System.out.println("Key=" + entry.getKey() + ", Value="+ entry.getValue());
try {
Field field = inputObj.getClass().getDeclaredField(entry.getKey());
} catch (NoSuchFieldException e) {
System.out.println("No such field: "+entry.getKey());
}
}
return someValue;
}
As shown above, the Map contains key-value pairs, where the key is going to be the field name (or variable name) from inputObj. I need to read the value of this field from inputObj. The datatype of the fields are String, int, Date, etc.
inputObj
public class UserDetails {
private int userId;
private String userName;
private Date joinedDate;
private Address homeAddress;
private String description;
// getters and setters
}
I can't do field.getLong or getChar, etc since the method is generic and doesn't know about the datatypes of the fields of inputObj.
I need to read the field values in the for loop and apply the business logic. Is this even possible? I tried a lot of ways but to no luck. Any references/pointers are appreciated.
how about this method in Filed :Object get(Object obj)
this method returns the value of the field represented by this Field, on the specified object.
I missed field.get(Object) method. This will resolve the issue.
field.getType() returns the type of the field (int.class, Date.class, etc). You can easily perform different actions depending on its return value.
Class<?> type = field.getType();
if(type == int.class) {
// load an int
} else if(type == Date.class) {
// load a Date
} else if(type == String.class) {
// load a String
}
// etc

Getting Error While Using Reflection to get Field Data

I am trying to fetch Field name as well as field value using Reflection.
I am passing dynamic classes as per operation needed.
I have made a method to fetch field name and value, i am getting field name but not getting field value.
when I am using following code it gives me an error java.lang.IllegalAccessException stating that can not access private member of class.
Following is my UPDATED code :
public String SerializeCommand(ICommand command){
StringBuilder command_text = new StringBuilder();
Field [] f = command.getClass().getDeclaredFields();
for(Field field : f){
field.setAccessible(true);
command_text.append(field.getName() + ",");
try {
System.out.println(field.get(command.getClass()));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return command_text.toString();
}
Here ICommand is a class name for it, suppose if operation is add then add class will be passed.
Any Idea what to do to solve this problem.
Instead of command.getClass() pass the object of command class. The value contains by object not by class
Please try this code.
public String SerializeCommand(ICommand command){
StringBuilder command_text = new StringBuilder();
Field [] f = command.getClass().getDeclaredFields();
try{
for(Field field : f){
field.setAccessible(true);
command_text.append(field.getName() + ",");
System.out.println("Value :: " + field.get(command));
}
}catch(IllegalArgumentException e){
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return command_text.toString();
}
Java also takes care of not being able to access private members, even when doing it via reflection. But constructors, methods, and fields are AccessibleObjects, which provides a method to flag the member as being accessible although it might be private:
field.setAccessible(true);
Afterwards you can read it and even set a new value on it.
An edit to make it clear. Consider the following simple record class:
public class Record {
private int length;
private String name;
public Record(int length, String name) {
this.length = length;
this.name = name;
}
public int getLength() { return length; }
public String getName() { return name; }
}
And now let's write a reflection test program:
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Record record = new Record(42, "42");
Field[] fields = record.getClass().getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + " => " + field.get(record));
}
}
}
This will result in an IllegalAccessException, as the private fields cannot be accessed. Now change the foreach loop a little:
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + " => " + field.get(record));
}
This time you will see expected output:
length => 42
name => 42
Your mistake also was to call the get method on the class and not on the object. This would be like this little modification:
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.getName() + " => " + field.get(record.getClass()));
}
This time you will see an IllegalArgumentException (not an IllegalAccessException).

Categories