Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting value of array using java reflection

I have created generic json parser using java reflection, but there is a error that i am not able to solve.

Method (at the bottom of this question), receives subclass of my custom Model class. I iterate through fields and set values from json. If subclass contains array property of some other class (which is again, subclass of Model), i've created small recursion to fill those objects.

Eg.

class UserModel extends Model
{
    @JsonResponseParam(Name="userName")
    public String Name;

    @JsonResponseParam(Name="friends")
    public FriendModel[] Friends;
}

At the end UserModel should be filled with Friends. (JsonResponseParam is custom anotation, and Name value is used as property for getting values from json)

Result of this method is IllegalArgumentException, and it is thrown on

field.set(t, values.toArray());

Here is the method:

protected <T extends Model> T getModel(T t)
{
    Field[] fields = t.getClass().getFields();

    for (Field field : fields) {
        Annotation an = field.getAnnotation(JsonResponseParam.class);

        if(an != null){
            try 
            {               
                if(field.getType() == boolean.class)                
                    field.setBoolean(t, t.getBool(((JsonResponseParam)an).Name()));

                if(field.getType() == String.class)
                    field.set(t,  t.getString(((JsonResponseParam)an).Name()));

                if(field.getType() == int.class)
                    field.setInt(t,  t.getInt(((JsonResponseParam)an).Name()));

                if(field.getType() == Date.class)
                    field.set(t,  t.getDate(((JsonResponseParam)an).Name()));

                if(field.getType().isArray()){

                    ArrayList<Model> modelArray = t.getModelArray(((JsonResponseParam)an).Name());
                    ArrayList<Model> values = new ArrayList<Model>();

                    for (Model model : modelArray) {

                        Class<? extends Model> arrayType = field.getType().getComponentType().asSubclass(Model.class);
                        Model m = arrayType.newInstance();
                        m.jsonObject = model.jsonObject;
                        model.getModel(m);
                        values.add(m);                      
                    }

                    field.set(t, values.toArray());
                }

            } catch (IllegalArgumentException e) { 
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    return t;
}

I am suspecting on class type inconsistency between field and values..

Thank you for your time.

like image 925
bajicdusko Avatar asked Nov 19 '13 22:11

bajicdusko


2 Answers

toArray() only return an Object[] so it cannot be assigned to any other array type.

What you want is

field.set(t, values.toArray(Array.newInstance(field.getType().getComponentType(), values.size()));

This will create an array of the type to match the field.

See Array.newInstance

like image 154
Peter Lawrey Avatar answered Sep 22 '22 14:09

Peter Lawrey


Basically, the pattern is as below:

// First, create the array
Object myArray = Array.newInstance(field.getType().getComponentType(), arraySize);

// Then, adding value to that array
for (int i = 0; i < arraySize; i++) {
    // value = ....
    Array.set(myArray, i, value);
}

// Finally, set value for that array field
set(data, fieldName, myArray);

The set function is taken from this stackoverflow question:

public static boolean set(Object object, String fieldName, Object fieldValue) {
    Class<?> clazz = object.getClass();
    while (clazz != null) {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(object, fieldValue);
            return true;
        } catch (NoSuchFieldException e) {
            clazz = clazz.getSuperclass();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }
    return false;
}

Apply the above code, we have:

if(field.getType().isArray()){
    // ....
    int arraySize = modelArray.size();
    Object values = Array.newInstance(field.getType().getComponentType(), modelArray.size());
    for (int i = 0; i < arraySize; i++) {
        // ......
        Array.set(values, i, m);                     
    }

    field.set(t, values);
}
like image 21
Hoa Nguyen Avatar answered Sep 21 '22 14:09

Hoa Nguyen