2011-05-22 71 views
2

我正在使用EmpireAvenue API在Android應用程序上工作。 API使用JSON,我正在使用GSON庫來解析API中的數據。 這裏的問題是:解析與GSON字段沒有特定結構的JSON

我有一個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結構嗎?

感謝您的幫助。

回答

1

Gson的當前解決方案有點涉及,需要實現自定義Instance Creator和/或自定義Deserializer。有關詳細信息,請參閱http://code.google.com/p/google-gson/issues/detail?id=231the 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(); 
    } 
}