解析具有GSON的字段没有特定结构的JSON

时间:2011-05-22 16:21:17

标签: java android json parsing gson

我正在使用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"
}

如您所见,与info字段关联的结构不同。有时它是一个对象,有时是一个数组。正如所料,GSON库在解析时会抛出错误。 你知道如何在字段改变结构时解析JSON结构吗?

感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

Gson的当前解决方案有点涉及,需要实现自定义实例创建器和/或自定义反序列化器。有关详细信息,请查看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();
  }
}