我正在使用EmpireAvenue API开发一个Android应用程序.
该API使用JSON,而我正在使用GSON库来解析该API中的数据.
这是问题所在:
我有一个像这样的JSON结构:
{
type: "earnings",
info: {
earnings: 64.09
dividends: 1277.34
gains: 1997.05
expenses: 4895.51
shares_bought: 210
shares_bought_user_count: 2
shares_sold: 0
shares_sold_user_count: 0
},
created: "2011-04-16 11:32:37"
},
{
type: "following",
info: [
{
ticker: "SOLPHE"
full_name: "Rodrigo Bermudez Salazar"
list_name: "My Recommended Buys"
},
{
ticker: "SOLPHE"
full_name: "Rodrigo Bermudez Salazar"
list_name: "My Watch List"
}
],
created: "2011-04-16 11:00:08"
}
如您所见,与信息字段关联的结构是不同的.有时是对象,有时是数组.不出所料,GSON库在解析时会引发错误.
您知道当字段更改结构时如何解析JSON结构吗?
谢谢你的帮助.
解决方法:
使用Gson的当前解决方案有些复杂,需要实现自定义实例创建器和/或自定义反序列化器.有关详细信息,请查看http://code.google.com/p/google-gson/issues/detail?id=231和the release notes on Hierarchical Type Adapters.我刚刚发布了一个示例,它用Gson响应Polymorphism with gson进行了多态反序列化.
希望Gson不久将拥有RuntimeTypeAdapter,以实现更简单的多态反序列化.有关更多信息,请参见http://code.google.com/p/google-gson/issues/detail?id=231.
另一方面,基于Jackson的解决方案还不错.
public class Foo
{
static String jsonInput =
"[" +
"{" +
"\"type\":\"earnings\"," +
"\"info\":" +
"{" +
"\"earnings\":64.09," +
"\"dividends\":1277.34," +
"\"gains\":1997.05," +
"\"expenses\":4895.51," +
"\"shares_bought\":210," +
"\"shares_bought_user_count\":2," +
"\"shares_sold\":0," +
"\"shares_sold_user_count\":0" +
"}," +
"\"created\":\"2011-04-16 11:32:37\"" +
"}," +
"{" +
"\"type\":\"following\"," +
"\"info\":" +
"[" +
"{" +
"\"ticker\":\"SOLPHE\"," +
"\"full_name\":\"RodrigoBermudezSalazar\"," +
"\"list_name\":\"MyRecommendedBuys\"" +
"}," +
"{" +
"\"ticker\":\"SOLPHE\"," +
"\"full_name\":\"RodrigoBermudezSalazar\"," +
"\"list_name\":\"MyWatchList\"" +
"}" +
"]," +
"\"created\":\"2011-04-16 11:00:08\"" +
"}" +
"]";
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new CamelCaseNamingStrategy());
DateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(dataFormat);
Collection<Thing> things = mapper.readValue(jsonInput, new TypeReference<Collection<Thing>>(){});
System.out.println(things);
}
}
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({@Type(value=Earnings.class, name="earnings"), @Type(value=Following.class, name="following")})
abstract class Thing
{
private Date created;
void setCreated(Date created)
{
this.created = created;
}
@Override
public String toString()
{
return String.format(
"[%1$s: created=%2$s, other attributes:%3$s]",
getClass().getSimpleName(), created, toStringAddenda());
}
abstract String toStringAddenda();
}
class Earnings extends Thing
{
private EarningsInfo info;
void setInfo(EarningsInfo info)
{
this.info = info;
}
@Override
String toStringAddenda()
{
return info.toString();
}
}
class Following extends Thing
{
private Collection<FollowingInfo> info;
void setInfo(Collection<FollowingInfo> info)
{
this.info = info;
}
@Override
String toStringAddenda()
{
return info.toString();
}
}
class FollowingInfo
{
private String ticker;
private String fullName;
private String listName;
void setTicker(String ticker)
{
this.ticker = ticker;
}
void setFullName(String fullName)
{
this.fullName = fullName;
}
void setListName(String listName)
{
this.listName = listName;
}
@Override
public String toString()
{
return String.format(
"[FollowingInfo: ticker=%1$s, fullName=%2$s, listName=%3$s]",
ticker, fullName, listName);
}
}
class EarningsInfo
{
private BigDecimal earnings;
private BigDecimal dividends;
private BigDecimal gains;
private BigDecimal expenses;
private int sharesBought;
private int sharesBoughtUserCount;
private int sharesSold;
private int sharesSoldUserCount;
void setEarnings(BigDecimal earnings)
{
this.earnings = earnings;
}
void setDividends(BigDecimal dividends)
{
this.dividends = dividends;
}
void setGains(BigDecimal gains)
{
this.gains = gains;
}
void setExpenses(BigDecimal expenses)
{
this.expenses = expenses;
}
void setSharesBought(int sharesBought)
{
this.sharesBought = sharesBought;
}
void setSharesBoughtUserCount(int sharesBoughtUserCount)
{
this.sharesBoughtUserCount = sharesBoughtUserCount;
}
void setSharesSold(int sharesSold)
{
this.sharesSold = sharesSold;
}
void setSharesSoldUserCount(int sharesSoldUserCount)
{
this.sharesSoldUserCount = sharesSoldUserCount;
}
@Override
public String toString()
{
return String.format(
"[EarningsInfo: earnings=%1$s, dividends=%2$s, gains=%3$s, expenses=%4$s, sharesBought=%5$s, sharesBoughtUserCount=%6$s, sharesSold=%7$s, sharesSoldUserCount=%8$s]",
earnings, dividends, gains, expenses, sharesBought, sharesBoughtUserCount, sharesSold, sharesSoldUserCount);
}
}
class CamelCaseNamingStrategy extends PropertyNamingStrategy
{
@Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return convert(defaultName);
}
@Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
return convert(defaultName);
}
@Override
public String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName)
{
return convert(defaultName);
}
private String convert(String defaultName)
{
char[] nameChars = defaultName.toCharArray();
StringBuilder nameTranslated = new StringBuilder(nameChars.length * 2);
for (char c : nameChars)
{
if (Character.isUpperCase(c))
{
nameTranslated.append("_");
c = Character.toLowerCase(c);
}
nameTranslated.append(c);
}
return nameTranslated.toString();
}
}