views:

3218

answers:

4

I am writing a deserialization method that converts xml to a Java object. I would like to do this dynamically and avoid writing hard coded references to specific types.

For example this is a simplified version of one of my classes.

public class MyObject { 
   public ArrayList<SubObject> SubObjects = new ArrayList<SubObject>();
}

Here is a stripped down version of the method:

public class Serializer {    
    public static <T> T fromXml(String xml, Class<T> c) {
       T obj = c.newInstance();       
       Field field = obj.getClass().getField("SubObjects");    
       //help : create instance of ArrayList<SubObject> and add an item
       //help#2 : field.set(obj, newArrayList);

       return obj;
    }
}

Calling this method would look like this:

MyObject obj = Serializer.fromXml("myxmldata", MyObject.class);

Forgive me if this is a trivial problem as I am a C# developer learning Java.

Thanks!

A: 

In java generics are just syntax sugar and they do not exist in runtime. This is why you cannot programmatically create generic objects in runtime. Try to create non generic list and cast (however I am not sure as my experience is limited with GWT).

Mike Chaliy
That's not quite true. If you have a class MyArray which extends ArrayList<Integer>, then you do have the type info at runtime. Also, it is possibly to instantiate an object at runtime which the code casts to a "parameterized object" - the cast will be meaningless though and your compiler will generate a type checked warning
oxbow_lakes
@Oxbow: A Class will store the parameterized type of its method signatures at runtimes. An individual collection does not I believe.
Jherico
A: 

You'll have to create a raw typed List (i.e. with no type parameters).

List subs = deserialize( ... );

If you were to set the raw typed List on your class directly:

obj.setSubObjects(subs); //You'll get an "unchecked cast" warning at compile time

Although the compiler will emit a warning, it is legal Java code and will run just fine at runtime because the parameter-type information is lost (i.e. at runtime, there is no difference between a List and a List<SomeClass>). You can of course set it reflectively also.

oxbow_lakes
+2  A: 

Should be something pretty close to:

Object list = field.getType().newInstance();

Method add = List.class.getDeclaredMethod("add",Object.class);

add.invoke(list, addToAddToList);

Hope this helps.

Gareth Davis
Thanks, this is what I was looking for. Can you also explain how I could get the type information for SubObject in the list instance?
That's the bit that isn't present, generic type information for collections is not available at runtime in Java. Generics are implemented in the compiler.
Gareth Davis
I guess the bit you are after is how to know what class to instantiate to add to you new list object? That information just isn't available, you could add it your self by creating an annotation and annotating the SubObjects field with it, something like @SerialisationType(SubObject.class)
Gareth Davis
I found it. If you call ((ParameterizedType)field.getGenericType()).toString() you can see the full name of the type which includes SubObject. I then parsed out the class name and created a new instance through Class.forName("SubObject").newInstance(). Very hacky, but it works.
+2  A: 

It seems like the real issue here is going from XML to java objects. If that is the case (and since you are new to Java), a better solution than rolling your own may be to investigate existing technologies such as: