Updated your writeInLogger method. I also added to check for the write method, else you get Class also as a property and the stack blows.
private static void writeInLogger(Object obj, String str) {
Class klazz = obj.getClass();
if (klazz.isPrimitive() || obj instanceof String || obj instanceof Integer || obj instanceof Double
|| obj instanceof Boolean)
System.out.println(str + obj.toString());
else {
try {
for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(klazz).getPropertyDescriptors()) {
if(propertyDescriptor.getWriteMethod() == null)
continue;
Method m = propertyDescriptor.getReadMethod();
if (m != null) {
Object object = m.invoke(obj);
if(object != null)
writeInLogger(object, str);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
}
Answer from Dakshinamurthy Karra on Stack OverflowUpdated your writeInLogger method. I also added to check for the write method, else you get Class also as a property and the stack blows.
private static void writeInLogger(Object obj, String str) {
Class klazz = obj.getClass();
if (klazz.isPrimitive() || obj instanceof String || obj instanceof Integer || obj instanceof Double
|| obj instanceof Boolean)
System.out.println(str + obj.toString());
else {
try {
for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(klazz).getPropertyDescriptors()) {
if(propertyDescriptor.getWriteMethod() == null)
continue;
Method m = propertyDescriptor.getReadMethod();
if (m != null) {
Object object = m.invoke(obj);
if(object != null)
writeInLogger(object, str);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
}
private static void writeInLogger(Object obj, String str) {
if (obj == null) {
System.out.println(str + "null");
return;
}
Class klazz = obj.getClass();
if (klazz.isPrimitive() || obj instanceof String
|| obj instanceof Integer || obj instanceof Double
|| obj instanceof Boolean)
System.out.println(str + obj.toString());
else {
try {
for (Field field : klazz.getDeclaredFields()) {
field.setAccessible(true);
Object f = field.get(obj);
field.setAccessible(false);
writeInLogger(f, str);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
}
Browse all fields in class and get the value object as the writeInLogger parameter.
getFields() only get public fields, getDeclaredFields to get all fields in class.
I guess this code might help. I haven't added the functionality for processing Map, but it works with List:
public static void main(String[] args) throws ClassNotFoundException
{
SomeClass s = new SomeClass();
Class c = s.getClass();
getMembers(c);
}
public static void getMembers(Class c) throws ClassNotFoundException
{
Field[] fields = c.getDeclaredFields();
for (Field f : fields)
{
if (f.getType().isPrimitive() || f.getType().equals(String.class))
{
System.out.println(c.getSimpleName() + ": " + f.getName() + " is a "+ f.getType().getSimpleName());
}
else
{
if (Collection.class.isAssignableFrom(f.getType()))
{
String s = f.toGenericString();
String type = s.split("\\<")[1].split("\\>")[0];
Class clazz = Class.forName(type);
System.out.println(c.getSimpleName()+ ": "+ f.getName()+ " is a collection of "+ clazz.getSimpleName());
getMembers(clazz);
}
else
{
System.out.println(c.getSimpleName() + ": " + f.getName() + " is a "+ f.getType().getSimpleName());
getMembers(f.getType());
}
}
}
}
Hope that helps.
One more simple example to get name value with reflection:
public void getNameValue() {
try {
RingtoneManager ringtoneManager = new RingtoneManager(this);
Field field = RingtoneManager.class.getField("ACTION_RINGTONE_PICKER");
String type = field.getType().getSimpleName();
String name = field.getName();
String value = (String)field.get(ringtoneManager);
Log.e(TAG,type + " " + name + " = " + value);
} catch (Exception e) {
e.printStackTrace();
}
}
To recursively modify all fields in an object annotated with a given annotation we have to determine first all fields.
Class.getFields() will return only "accessible public fields". This doesn't match the requirement. Therefore we have to use Class.getDeclaredFields(). This in turn returns only the fields "declared by the class". In order to get all fields we have to traverse the type hierarchy of a target object upwards and process for each type its declared fields.
For every field we have to distinguish some cases:
- A
Fieldis annotated with the specified annotation. Then the given value has to be applied to the field. In order to set the value, the field has to be (made) accessible and non final. - A
Fieldis not annotated with the specified annotation. Then there are three cases to distinguish:- The field has a primitive type. Then it isn't an object, which could contain a
Stringfield. - The field has an array type. Then it cannot contain a
Stringfield. - Else: The field has a reference type. Then it has to be (made) accessible, to check if it has a value set and in this case to process its fields recursively for 1) and 2) again.
- The field has a primitive type. Then it isn't an object, which could contain a
To check whether an annotation is set on a field, we use Field.getAnnotation(myAnnotationClass) where myAnnotationClass is declared as Class<MyAnnotation>. But we have to be aware of something additional:
Field.getAnnotation(myAnnotationClass) will return the fields's annotation only if
- it's set (quite self-explanatory)
MyAnnotationitself is annotated with@Retention(RetentionPolicy.RUNTIME).
The corresponding code would look like that:
public static <T extends Annotation> void setValueOnAnnotatedFields(String s, Class<T> annotationClass, Object target) {
if (target != null) {
// traversing the type hierarchy upward to process fields declared in classes target's class extends
Class<?> clazz = target.getClass();
do {
setValueOnAnnotatedDeclaredFields(s, annotationClass, target, clazz);
}
while ((clazz = clazz.getSuperclass()) != null);
}
}
private static <T extends Annotation> void setValueOnAnnotatedDeclaredFields(String s, Class<T> annotationClass, Object target, Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.getAnnotation(annotationClass) != null) {
set(field, target, s);
} else if (!field.getType().isPrimitive() && !field.getType().isArray()) {
setValueOnAnnotatedFields(s, annotationClass, get(field, target));
}
}
}
protected static void set(Field field, Object target, String value) {
try {
makeFiieldAccessibleNotFinal(field, target);
field.set(target, value);
}
catch (Exception e) {
String message = String.format("Failed to set the value on field %s", target.getClass().getSimpleName() + "." + field.getName());
throw new IllegalStateException(message, e);
}
}
protected static Object get(Field field, Object target) {
try {
makeFiieldAccessibleNotFinal(field, target);
return field.get(target);
}
catch (Exception e) {
String message = String.format("Failed to get the value on field %s", target.getClass().getSimpleName() + "." + field.getName());
throw new IllegalStateException(message, e);
}
}
protected static void makeFiieldAccessibleNotFinal(Field field, Object target) {
try {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
catch (Exception e) {
String message = String.format("Failed to remove final declaration of field %s", field.getName());
throw new IllegalStateException(message, e);
}
try {
field.setAccessible(true);
}
catch (Exception e) {
String message = String.format("Failed to make field %s accessible", field.getName());
throw new IllegalStateException(message, e);
}
}
An example:
public static void main(String[] args) {
A a = new A();
System.out.println(a); // s: null / b: [null]
setValueOnAnnotatedFields("1", MyAnnotation.class, a);
System.out.println(a); // s: 1 / b: [null]
a.b = new B();
setValueOnAnnotatedFields("2", MyAnnotation.class, a);
System.out.println(a); // s: 2 / b: [a: 2 / b: null]
}
This implies class A to have toString() implemented like that:
@Override
public String toString() {
return "s: " + s + " / b: [" + Optional.ofNullable(b).map(B::toString).orElse("null") + "]";
}
And class B to have toString() implemented like that:
@Override
public String toString() {
return "a: " + a + " / b: " + b;
}
You can use Java reflection API to inspect the fields of your class:
Get the declared annotations foreach field;
Check if you have your annotation..
Well, it is not a difficult thing to do thanks to Reflection API.
You can do it like this. You need to add more condition as per your requirement in filter:
public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
fields.addAll(
Arrays.stream(type.getDeclaredFields())
.filter(field -> field.isAnnotationPresent(NotNull.class))
.collect(Collectors.toList())
);
if (type.getSuperclass() != null) {
getAllFields(fields, type.getSuperclass());
}
return fields;
}
Call example:
List<Field> fieldList = new ArrayList<>();
getAllFields(fieldList,B.class);
You can use the "reflections" library.
https://github.com/ronmamo/reflections
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage("your.packageName"))
.setScanners(new SubTypesScanner(), new TypeAnnotationsScanner(), new FieldAnnotationsScanner()));
Set<Field> fields =
reflections.getFieldsAnnotatedWith(YourAnnotation.class);
fields.stream().forEach(field -> {
System.out.println(field.getDeclaringClass());
System.out.println(field.getName());
});
You should pass the object to get method of the field, so
import java.lang.reflect.Field;
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(object);
Like answered before, you should use:
Object value = field.get(objectInstance);
Another way, which is sometimes prefered, is calling the getter dynamically. example code:
public static Object runGetter(Field field, BaseValidationObject o)
{
// MZ: Find the correct method
for (Method method : o.getMethods())
{
if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3)))
{
if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase()))
{
// MZ: Method found, run it
try
{
return method.invoke(o);
}
catch (IllegalAccessException e)
{
Logger.fatal("Could not determine method: " + method.getName());
}
catch (InvocationTargetException e)
{
Logger.fatal("Could not determine method: " + method.getName());
}
}
}
}
return null;
}
Also be aware that when your class inherits from another class, you need to recursively determine the Field. for instance, to fetch all Fields of a given class;
for (Class<?> c = someClass; c != null; c = c.getSuperclass())
{
Field[] fields = c.getDeclaredFields();
for (Field classField : fields)
{
result.add(classField);
}
}
no, you need to write it yourself. It is a simple recursive method called on Class.getSuperClass():
public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
fields.addAll(Arrays.asList(type.getDeclaredFields()));
if (type.getSuperclass() != null) {
getAllFields(fields, type.getSuperclass());
}
return fields;
}
@Test
public void getLinkedListFields() {
System.out.println(getAllFields(new LinkedList<Field>(), LinkedList.class));
}
public static List<Field> getAllFields(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}