开发者

Parse JSON with no specific structure for a field with GSON

开发者 https://www.devze.com 2023-03-07 20:22 出处:网络
I am working on an Android application, using the E开发者_StackOverflowmpireAvenue API. The API uses JSON and I\'m using the GSON library to parse the data from the API.

I am working on an Android application, using the E开发者_StackOverflowmpireAvenue API. The API uses JSON and I'm using the GSON library to parse the data from the API. Here is the problem:

I have a JSON structure like this:

{
    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"
}

As you can see, the structure associated with the info field is different. Sometimes it's an object, sometimes an array. As expected, the GSON library throws errors when parsing. Do you know how to parse a JSON structure with when a field changes structure ?

Thanks for your help.


The current solution with Gson is a bit involved, requiring implementation of a custom Instance Creator and/or a custom Deserializer. Take a look at http://code.google.com/p/google-gson/issues/detail?id=231 and the release notes on Hierarchical Type Adapters for details. I just posted an example of polymorphic deserialization with Gson in response to Polymorphism with gson.

Gson hopefully will soon have the RuntimeTypeAdapter for simpler polymorphic deserialization. See http://code.google.com/p/google-gson/issues/detail?id=231 for more info.

On the other hand, a Jackson-based solution isn't so bad.

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();
  }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号