BeanInfo componentBeanInfo = null;
List<String> propNames =new ArrayList<String>();
Object nestedObject=null;
try {
componentBeanInfo = Introspector.getBeanInfo(sourceObject.getClass());
final PropertyDescriptor[] props=componentBeanInfo.getPropertyDescriptors();
String [] parameters = getParameters(); //ObjectA.code="abc",ObjectA.type="single"
for (String parameter : parameters) {
boolean isNestedField = isNestedPropertyRead(parameter);
for(PropertyDescriptor prop : props){
if(isNestedField){
String[] fullParam = parameter.split("\\.");//ObjectA.code
String nestedObj = fullParam[0];//ObjectA
String nestObjField = fullParam[1];//code
if(nestedObj.equalsIgnoreCase(prop.getName())){
Class<?> classType = Class.forName(prop.getPropertyType().getName());
BeanInfo nestedBeanInfo;
nestedBeanInfo = Introspector.getBeanInfo(classType);
final PropertyDescriptor[] nestedProps =nestedBeanInfo.getPropertyDescriptors();
for(PropertyDescriptor nestedProp : nestedProps){
if(nestedProp.getName().equalsIgnoreCase(nestObjField)){
Class<?> nestedClassType = Class.forName(nestedProp.getPropertyType().getName());
Object value = convertToObject(nestedClassType,value(parameter));
try {
/*if(isNewProperty(prop.getName(),propNames)){
nestedObject = classType.newInstance();
}*/
if(nestedObject == null) {
nestedObject = classType.newInstance();
}
nestedProp.getWriteMethod().invoke(nestedObject, value);
prop.getWriteMethod().invoke(sourceObject,nestedObject );
} catch (IllegalArgumentException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
break;
}
}
break;
}
}
else if(prop.getName().equalsIgnoreCase(parameter)){
try {
Class<?> classType = Class.forName(prop.getPropertyType().getName());
Object value = convertToObject(classType,value(parameter));
prop.getWriteMethod().invoke(sourceObject, value);
break;
} catch (IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}catch (IntrospectionException | IllegalAccessException e) {
e.printStackTrace();
}
In above code snippet I'm facing issue w.r.t following lines -
nestedObject = classType.newInstance();
This creates new Instance every time and because of which i end up up
setting values to new nested objects each time overriding the
previous object. How can i avoid this and set values to already
created object in loop. Can anyone please suggest how can i get it
working?
I'll definitely be changing below piece of code also as generic one
rather than limiting to one instance of ' . ' as delimiter.But
thought of getting the looping logic right first.
String[] fullParam = parameter.split("\\.");//ObjectA.code
String nestedObj = fullParam[0];//ObjectA
String nestObjField = fullParam[1];//code
UPDATE-1- #javaguy -
My object structure looks like below-
public class OuterObject {
private String field1;
private ObjectA field2;
private ObjectB field3;
private String field4;
...// can have lot of nested objects of different types like ObjectA,ObjectB etc
}
public class ObjectA {
private String field1;
private int field2;
private String field3;
...
}
public class ObjectB {
private String field1;
private String field2;
private String field3;
...
}
As per your logic, nestedObject will have instance of ObjectA when
ObjectA's field (say field2) is encountered first.When we get ObjectB's field next (say field1)
we check if nestedObject is null and since it is not null we don't
create new instance for ObjectB.Because of this we end up setting
ObjectB's field value (field1) in ObjectA in following line-
nestedProp.getWriteMethod().invoke(nestedObject, value); This
results in error - java.lang.IllegalArgumentException: object is not
an instance of declaring class
you need to declare Object nestedObject=null; outside the loop and then create the instance only once by checking that it is null as shown below:
if(nestedObject == null) {
nestedObject = classType.newInstance();
}
I could make it work after adding following lines of code -
Object nestedObject = prop.getReadMethod().invoke(sourceObject);
if(nestedObject == null){
nestedObject = classType.newInstance();
}
Related
I want to make a Map (String ,Object) like this
{AssessmentId=0, Physical_name='ram', Physical_height=20, Physical_weight=60}
from my Pojo Class - InitialAssessment
public class InitialAssessment {
private long AssessmentId;
private String physical_name;
private String physical_gender;
private int physical_height;
private float physical_weight;
// all getter And setter is Created here
}
without using any external Library like Gson etc.
You can use this approach:
public Map getMapFromPojo(InitialAssessment assessment) throws Exception {
Map<String, Object> map = new HashMap<>();
if (assessment != null) {
Method[] methods = assessment.getClass().getMethods();
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("get") && !name.equalsIgnoreCase("getClass")) {
Object value = "";
try {
value = method.invoke(assessment);
map.put(name.substring(name.indexOf("get") + 3), value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return map;
}
return null;
}
It will give you map for pojo class like this:
Output:
{AssessmentId=0, Physical_name='ram', Physical_gender='Male' , Physical_height=20, Physical_weight=60}
I am try to use reflect to create some instance through the json configuration:
config.json:
{
"clazz":com.kk.Manager",
"args":{
"path":"/home/kk/a.txt",
"scale":1
}
}
Implementation:
packge com.kk;
public class Manager{
private File file;
private int scale;
public Manager(String path,int scale){
this.file = new File(path)
this.scale=scale;
//other initialization work omitted
}
}
private static Object buildObject(String clazz, JSONObject args) {
Object instance;
try {
Class clas = Class.forName(clazz);
if (args == null) {
instance = clas.newInstance();
} else {
Class[] pamas = new Class[args.length()];
Object[] vals = new Object[args.length()];
Iterator<String> parameterIt = args.keys();
int i = 0;
while (parameterIt.hasNext()) {
String fd = parameterIt.next();
Object val = args.get(fd);
pamas[i] = val.getClass();
vals[i] = val;
i++;
}
instance = clas.getConstructor(pamas).newInstance(vals);
}
return instance;
} catch (Exception e) {
return null;
}
return null;
}
After I parse the json config file, I will call it like this:
String clazz = parent.getString("clazz");
JSONObject args = parent.getJSONObject("args")
buildObject(clazz,args);
While the code throw error:
java.lang.NoSuchMethodException: com.kk.Manager.<init>(java.lang.Integer, java.lang.String)
It seem that the constructor of Manager is called with parameters in wrong order.
After google, it seems that the order of keys in a JSONObject is undefined.
So I wonder if I can get the constructor by the parameter name, something like:
Class cls = Class.forName(clazz);
cls.getConstructor("path","scale");
Or if not, how can I fix this without add the setter method?
I'm trying to use flexjson to deserialize a string I get from a web call. The problem is that a few elements in there have a dot in the property/key for example:
[{...
"contact.name": "Erik Svensson",
"contact.mail": "erik.svensson#foo.bar",
"contact.phone": "0731123243",
...}]
Now everything else falls in place except these strings with the dots, they end up null in my target class. I'm guessing it's because it doesn't know what to map them to as I can't declare a variable in my container class that has a dot.
This is the code I'm runnign to deserialize now,
mData = new JSONDeserializer<List<Thing>>()
.use("values", Thing.class)
.deserialize(reader);
How do I modify this to catch the strings with the dot and put them in my Things class as:
String contactName;
String contactMail;
String contactPhone;
// getters&setters
Note I don't have any control over the Serialization..
OK So I've solved this but I had to abandon flexJson. Searched all over the place for a simple way but couldn't find one.
Instead I went with Jackson and this is what I ended up with:
ObjectMapper mapper = new ObjectMapper();
mThings = mapper.readValue(url, new TypeReference<List<Thing>>() {});
And in my class Thing:
#JsonProperty("contact.name")
private String contactName;
#JsonProperty("contact.mail")
private String contactMail;
#JsonProperty("contact.phone")
private String contactPhone;
// getters and setters..
If anyone knows how to do this with FlexJson feel free to post an answer, I would like to see it.
As I was curious, too, if this type of assignment can be done easily, I've played with some code, and this is what I came up with. (I'm posting it here because maybe it's helpful for somebody having some related question, or just as a point to start from.)
The PrefixedObjectFactory (see below) will cut off a fixed prefix from the JSON object's field name and use this name to find a matching bean property. The code can be easily changed to do a replacement instead (e.g. setting the first letter after a . to uppercase and remove the .)
It can be used like this:
List<Thing> l = new JSONDeserializer<List<Thing>>().use("values", new PrefixedObjectFactory(Thing.class, "contact.")).deserialize(source);
The code:
import flexjson.ObjectBinder;
import flexjson.ObjectFactory;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Type;
import java.util.Map;
public class PrefixedObjectFactory<T> implements ObjectFactory {
protected Class<T> clazz;
protected String prefix;
public PrefixedObjectFactory(Class<T> c, String prefix) {
this.clazz = c;
this.prefix = (prefix == null) ? "" : prefix;
}
#Override
public Object instantiate(ObjectBinder context, Object value, Type targetType, Class targetClass) {
try {
Class useClass = this.clazz;
T obj = (T)useClass.newInstance();
if (value instanceof Map) {
// assume that the value is provided as a map
Map m = (Map)value;
for (Object entry : m.entrySet()) {
String propName = (String)((Map.Entry)entry).getKey();
Object propValue = ((Map.Entry)entry).getValue();
propName = fixPropertyName(propName);
propValue = fixPropertyValue(propValue);
assignValueToProperty(useClass, obj, propName, propValue);
}
} else {
// TODO (left out here, to keep the code simple)
return null;
}
return obj;
} catch (Exception ex) {
return null;
}
}
protected String fixPropertyName(String propName) {
if (propName.startsWith(this.prefix)) {
propName = propName.substring(this.prefix.length());
}
return propName;
}
protected Object fixPropertyValue(Object propValue) {
return propValue;
}
protected PropertyDescriptor findPropertyDescriptor(String propName, Class clazz) {
try {
return new PropertyDescriptor(propName, clazz);
} catch (Exception ex) {
return null;
}
}
protected void assignValueToProperty(Class clazz, Object obj, String propName, Object propValue) {
try {
PropertyDescriptor propDesc = findPropertyDescriptor(propName, clazz);
if (propDesc != null) {
propDesc.getWriteMethod().invoke(obj, propValue);
}
} catch (Exception ex) {
}
}
}
I have a class as below.
public class Emp{
private String name;
private String age;
//setters and getters
}
Have one more class below.
public class Student extends Emp{
private int marks;
//setters and getters
}
is there anyway to get the fields of a subclass using super class using java Reflection?
I need to get Student fields using Emp instance.
we can get super class fields as below:
subClass.getClass().getSuperclass().getDeclaredFields();
similarly can i get sub class fields using super class?
Is it possible?
Thanks!
I may have misunderstood your question. Do you want to do something like the following?
Emp e = new Student(...);
[do something with e]
foo = e.marks;
If yes, do it like this:
foo = ((Emp)e).marks;
However, if you want to do something like the following:
Emp e = new Emp(...);
[do something with e]
e.marks = ....
Then no, it's not possible, and I'd suspect your internal model of java's object model is either incomplete or flawed.
In theory there is a very complicated and costly way by retrieving all loaded classes and checking which of them are derived from Emp and contain the field. If the desired class wasn't loaded yet this may not help either.
Not directly, you have to write a helper method to that.
You take a class and the field name (and possibly type) as parameters, then look for that field in the given class. If you cant find it, you take the class's superclass and repeat from the beginning. You do this until you either found the field, or getSuperClass() returned null (meaning you reached the root of the inheritance tree).
This example demonstrates how to call find and call a specified method on an object. You can easily extract and adapt the logic for fields.
public static Object call(final Object instance,
final String methodName,
final Class<?>[] signature,
final Object[] args) {
try {
if (instance == null)
return null;
Class<?> instanceClass = instance.getClass();
while (instanceClass != null) {
try {
final Method method = instanceClass.getDeclaredMethod(methodName, signature);
if (!method.isAccessible())
method.setAccessible(true);
return method.invoke(instance, args);
} catch (final NoSuchMethodException e) {
// ignore
}
instanceClass = instanceClass.getSuperclass();
}
} catch (final Throwable e) {
return null;
}
return null;
}
Is it what you want? But beware of using field.setAccesible.
Parent class:
public class ParentClass {
private String parentField = "parentFieldValue";
public void printFields() throws IllegalAccessException {
Field[] fields = getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue = field.get(this);
if (fieldValue instanceof String) {
String stringValue = (String) fieldValue;
System.out.println(stringValue);
}
}
}
}
Child class:
public class ChildClass extends ParentClass {
private String childField = "childFieldValue";
}
Usage:
public class Main {
public static void main(String[] args) throws IllegalAccessException {
ParentClass pc = new ParentClass();
ChildClass cc = new ChildClass();
pc.printFields();
cc.printFields();
}
}
This is the final solution!
#NonNull
public static List<Class<?>> getSubClasses() {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
String method = trace[3].getMethodName();
if (!"<init>".equals(method)) {
throw new IllegalStateException("You can only call this method from constructor!");
}
List<Class<?>> subClasses = new ArrayList<>();
for (int i = 4; i < trace.length; i++) {
method = trace[i].getMethodName();
if ("<init>".equals(method)) {
try {
subClasses.add(Class.forName(trace[i].getClassName()));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} else {
break;
}
}
return subClasses;
}
this are some examples of usage:
class a {
public a(){
print(getSubClasses());
}
}
class b extends a{
}
class c extends b{
}
And the result is
new a() -> []
new b() -> [b.class]
new c() -> [b.class, c.class]
I have an object A1 of type A. I dynamically find that out , that object A1 is of type A. I now have a property say "Name" which I want to access from A1 , how do I do it ?
Now the biggest problem is that the object A1 can even be of type B. If it is of type B then I will have to obtain the value "Address". Now How I resolve this ?
Below code does the type check ,
public static void testing(Object A1, String s) s - Classtype
{
try{
Class c = Class.forName(s);
if( c.isInstance(A1)) //
{
//Now I know that A1 is of the type C. But I dont know what type 'c' is (whether type A or type B. Because Only then I can access the appropriate member.) Like I said, type A contain 'name' and type B contains address.
// The access may not only be a member but also a method .
}
}catch (Exception e){ System.out.println(e);}
}
Any pointers would help a lot . thanks
You can know the declared fields of class
Class cls = Class.forName("MyClass");
Field fieldlist[] = cls.getDeclaredFields();
Documentation
This kind of thing is tricky and error-prone if you do it manually. You should use one of the many BeanUtils / BeanHelper classes that almost every major framework contains. Here is my own quick example implementation which you can use if you want to:
public final class BeanHelper{
/**
* Return a map of an object's properties (key: property name, value:
* property type).
*
* #exception NullPointerException
* if bean is null
*/
public static Map<String, Class<?>> describeProperties(final Object bean){
if(bean == null){
throw new NullPointerException();
}
final Map<String, Class<?>> map;
final Class<?> beanClass = bean.getClass();
if(PROPERTIES_CACHE.containsKey(beanClass)){
map = PROPERTIES_CACHE.get(beanClass);
} else{
final PropertyDescriptor[] propertyDescriptors =
getBeanInfo(beanClass);
if(propertyDescriptors.length == 0){
map = Collections.emptyMap();
} else{
final Map<String, Class<?>> innerMap =
new TreeMap<String, Class<?>>();
for(final PropertyDescriptor pd : propertyDescriptors){
innerMap.put(pd.getName(), pd.getPropertyType());
}
map = Collections.unmodifiableMap(innerMap);
}
PROPERTIES_CACHE.put(beanClass, map);
}
return map;
}
private static PropertyDescriptor[] getBeanInfo(final Class<?> beanClass){
try{
return Introspector.getBeanInfo(beanClass, Object.class)
.getPropertyDescriptors();
} catch(final IntrospectionException e){
throw new IllegalStateException(
MessageFormat.format(
"Couldn''t access bean properties for class {0}",
beanClass),
e);
}
}
/**
* Retrieve a named property from a specified object.
*
* #return the property
* #exception NullPointerException
* if one of the arguments is null
* #exception IllegalArgumentException
* if there is no such property
*/
public static Object getBeanProperty(final Object bean,
final String property){
if(bean == null || property == null){
throw new NullPointerException();
}
final Class<?> beanClass = bean.getClass();
Map<String, PropertyDescriptor> propMap;
if(PROPERTY_DESCRIPTOR_CACHE.containsKey(beanClass)){
propMap = PROPERTY_DESCRIPTOR_CACHE.get(beanClass);
} else{
final PropertyDescriptor[] beanInfo = getBeanInfo(beanClass);
if(beanInfo.length == 0){
propMap = Collections.emptyMap();
} else{
propMap =
new HashMap<String, PropertyDescriptor>(beanInfo.length);
for(final PropertyDescriptor pd : beanInfo){
propMap.put(pd.getName(), pd);
}
}
PROPERTY_DESCRIPTOR_CACHE.put(beanClass, propMap);
}
if(!propMap.containsKey(property)){
throw new IllegalArgumentException(
MessageFormat.format(
"Class {0} does not have a property ''{1}''",
beanClass,
property));
}
return invokeMethod(propMap.get(property).getReadMethod(), bean);
}
private static Object invokeMethod(final Method method,
final Object bean,
final Object... args){
try{
return method.invoke(bean, args);
} catch(final IllegalArgumentException e){
throw e;
} catch(final IllegalAccessException e){
throw new IllegalStateException(
MessageFormat.format(
"Method not accessible: {0}",
method),
e);
} catch(final InvocationTargetException e){
throw new IllegalStateException(
MessageFormat.format(
"Error in method: {0}",
method),
e);
}
}
private static final Map<Class<?>, Map<String, Class<?>>>
PROPERTIES_CACHE =
new ConcurrentHashMap<Class<?>, Map<String, Class<?>>>();
private static final Map<Class<?>, Map<String, PropertyDescriptor>>
PROPERTY_DESCRIPTOR_CACHE =
new ConcurrentHashMap<Class<?>, Map<String, PropertyDescriptor>>();
private BeanHelper(){
}
}
Test Code:
public static void main(final String[] args){
class Dummy{
private String foo = "bar";
private String baz = "phleem";
public String getFoo(){
return foo;
}
public void setFoo(final String foo){
this.foo = foo;
}
public String getBaz(){
return baz;
}
public void setBaz(final String baz){
this.baz = baz;
}
}
final Object dummy = new Dummy();
final Map<String, Class<?>> beanProperties =
BeanHelper.describeProperties(dummy);
System.out.println(beanProperties);
for(final String key : beanProperties.keySet()){
System.out.println(MessageFormat.format("{0}:{1}",
key,
BeanHelper.getBeanProperty(dummy, key)));
}
}
Output:
{baz=class java.lang.String, foo=class java.lang.String}
baz:phleem
foo:bar
Look at this: BeanUtils
myUser.setName("Bob");
// can instead be written:
BeanUtils.setProperty(myUser, "name", "Bob");
// and then retrieve:
BeanUtils.getProperty(myUser, "name");
The fields are typically private. So, to access them you have to call
field.setAccessible(true);
BTW, are you sure you really wish to use reflection in this case? Did you probably think about declaring interface? The class (implementation) can be still loaded dynamically.
For example: NameAccessor and AddressAccessor are interfaces.
FirstClass and SecondClass are classes. Let's assume that FirstClass implements NameAccessor and SecondClass implements both interfaces.
Now you can say:
Class clazz = Class.forName("SecondClass");
Object obj = clazz.newInstance();
//......
String name = ((NameAccessor)obj).getName();
String address = ((AddressAccessor)obj).getAddress();
I think (IMHO) that this solution is better than accessing private fields using reflection.