Skip to content

Xml parser should support parsing derived classes #73

Description

@GoogleCodeExporter

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.com on 15 Dec 2010 at 10:28

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions