External references, such as a standards document, or specification?
Java environments (e.g. Java 6, Android 2.2, App Engine 1.3.7, or All)?
Please describe the feature requested.
Problem
=======
When Xml.java parses an xml response, it should support deserializing classes
when inheritance is involved.
E.g. consider the following example response (simplified for brevity).
<Response>
<Entries>
<Entry type='UserById'>
<Id>xxxx</Id>
<Name>xxxx</Name>
</Entry>
<Entry type='UserByEmail'>
<EmailAddress>xxxx</EmailAddress>
<Name>xxxx</Name>
</Entry>
<Entry type='UserByDomain'>
<Domain>xxxx</Domain>
<Name>xxxx</Name>
</Entry>
</Entries>
</Response>
Now, my java classes could look like follows:
public class UserBase {
@Key("Name")
public string name;
}
class UserById extends UserBase {
@Key("Id")
public string id;
}
class UserByEmail extends UserBase {
@Key("EmailAddress")
public string email;
}
class UserByDomain extends UserBase {
@Key("Domain")
public string domain;
}
public class UserBaseList {
@Key("Entry")
public List<UserBase> entries;
}
public class Response {
@Key("Entries")
public UserBaseList list;
}
However, the current Xml parser provides no way of actually parsing objects
into this format, since it has no way of knowing the actual types of UserBase
from the xml at parse time.
The only way to make things work right now is to define the class as
public class UserBase {
@Key("Name")
public string name;
@Key("Id")
public string id;
@Key("EmailAddress")
public string email;
@Key("Domain")
public string domain;
}
which works, but isn't quite elegant, especially when the class has several
fields, or if the objects map to business entities known to the end user.
Suggested workaround
====================
1. Have a TypeResolver annotation to help Xml.java parse the types properly.
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeResolver {
Class<?> value();
}
2. Define a FieldTypeResolver interface.
public interface FieldTypeResolver {
Class<?> resolve(XmlPullParser parser, XmlNamespaceDictionary namespace);
}
3. Annotate the field under question with this type.
public class UserBaseList {
@TypeResolver(UserBaseTypeResolver.class)
@Key("Entry")
public List<UserBase> entries;
}
4. Implement a custom FieldTypeResolver for resolving UserBaseType.
public class UserBaseTypeResolver implements FieldTypeResolver {
public Class<?> resolve(XmlPullParser parser, XmlNamespaceDictionary namespace) {
String type = parser.getAttributeValue("", "type");
if (type.compareTo("UserByEmail") == 0) {
return UserByEmail.class;
} else if (type.compareTo("UserById") == 0) {
return UserById.class;
} else if (type.compareTo("UserByDomain") == 0) {
return UserByDomain.class;
} else {
return UserBase.class;
}
}
}
5. Modify Xml.java to query the field for a FieldTypeResolver while
deserializing the objects. (Simplified for brevity).
Class<?> fieldClass = null;
field == null ? null : field.getType();
FieldTypeResolver resolver = getFieldResolver(field);
if (resolver != null) {
fieldClass = resolver.resolve(parser, namespaceDictionary);
} else {
fieldClass = field == null ? null : field.getType();
}
private static FieldTypeResolver getFieldResolver(Field field) {
TypeResolver resolver = field.getAnnotation(TypeResolver.class);
if (resolver == null) {
return null;
}
try {
return (FieldTypeResolver) resolver.value().newInstance();
} catch (InstantiationException exception) {
return null;
} catch (IllegalAccessException exception) {
return null;
}
}
Original issue reported on code.google.com by
an...@google.comon 15 Dec 2010 at 10:28