Java Reflection and OpenAPI generated class
As developers, we experience different challenges while working on tasks, no matter how old or experienced we are. This is when we knock at stackoverflow’s door (my personal favourite) and others like baeldung, chatGPT, cursor, etc.
On a recent task, while confidently writing a generic method and using Java Reflection, I happened to stumble upon the NoSuchFieldException thrown by Reflection.
Let me demonstrate..!!
public class Student {
private int rollNo;
private String name;
private String phoneNo;
//constructor
//getter and setter
}
For a generic method, where any type of object can be passed as a parameter, Java Reflection allows us to get the specific field name.
public static <T> void testReflection(T param){
try {
Field sField = param.getClass().getDeclaredField("name");
sField.setAccessible(true);
System.out.println(sField.get(param));
}catch (NoSuchFieldException | IllegalAccessException ex){
System.out.println("Error occurred due to: "+ex);
}
}
public static void main(String[] args){
Student student = new Student(1, "JavaTest", "00000000");
testReflection(student);
}
//Output: JavaTest
Since it has the field “name” so the output will be the name of the student object passed.
This is a simple one, the real challenge is when we write a yaml file to define the API using OpenAPI and the class is auto generated upon executing the yaml file.
Let’s take the same Student and try to define in OpenAPI yaml file…
#some other fields and declaration
Student:
type: object
properties:
RollNo:
type: integer
format: int64
Name:
type: string
format: string
PhoneNo:
type: string
format: string
The auto generated class looks something like this..
public class Student{
public static final String SERIALIZED_NAME_ROLLNO = "RollNo";
@SerializedName(SERIALIZED_NAME_ROLLNO)
private Integer rollNo = null;
public static final String SERIALIZED_NAME_NAME = "Name";
@SerializedName(SERIALIZED_NAME_NAME)
private String name = null;
public static final String SERIALIZED_NAME_PHONENO = "PhoneNo";
@SerializedName(SERIALIZED_NAME_PHONENO)
private String phoneNo = null;
//getters and setters
}
Now I use the same process testReflection and this time it throws error NoSuchFieldException!!!!
So, I modified the code a bit to know the fields it contained.
Field[] fields = param.getClass().getDeclaredFields();
for(Field field: fields){
System.out.println(field.getName());
}
I was expecting the fields declared in the class Student would be printed but instead the fields like logger, categoryHelper, jacocoData was printed.
I scratched my head (confused!!) and started looking for solutions and I finally came to the following conclusion.
If the class is a proxy or instrumentation-related class, it may not directly be the class that contains your
@SerializedName
fields. This often happens in frameworks that use proxies (e.g., Spring, Hibernate, or JaCoCo).
Instead of using getClass() directly on student object, I need to get the super class using getTargetClass(object) which belongs to org.springframework.aop.support.AopUtils and then use getSuperclass().getDeclaredFields() to read the fields of the Student class.
Class<?> class = org.springframework.aop.support.AopUtils.getTargetClass(param);
Field sField = class.getSuperclass().getDeclaredField("name");
That’s it!!! It worked…!!!!
Hope the content of this article has successfully fulfilled its intended objectives and effectively addressed the goals it was meant to achieve.
For other trivial problem solution related to Kubernetes, dive into the following articles:
[1] KIND: Kubernetes In Docker — Pulling Image From Private Insecure Registry