yaelFriedmann / google-api-java-client

Automatically exported from code.google.com/p/google-api-java-client
0 stars 0 forks source link

Xml parser should support parsing derived classes #73

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago

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

GoogleCodeExporter commented 9 years ago
Thanks for the great feature suggestion.  This could be particularly useful for 
@gd:kind attribute which is used in multi-kind entries in the Picasa Web Albums 
Data API.  See MultiKindFeedParser for an example.  You proposal generalizes 
that.

Original comment by yan...@google.com on 16 Dec 2010 at 8:15

GoogleCodeExporter commented 9 years ago

Original comment by yan...@google.com on 8 Jan 2011 at 6:30