Разбор JSON без конкретной структуры для поля с GSON - PullRequest
2 голосов
/ 22 мая 2011

Я работаю над приложением Android, используя EmpireAvenue API.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, когда поле меняет структуру?

Спасибо за вашу помощь.

1 Ответ

1 голос
/ 23 мая 2011

Текущее решение с Gson немного сложное, требующее реализации пользовательского Instance Creator и / или специального десериализатора.Посмотрите подробности в примечаниях к выпуску адаптеров иерархического типа http://code.google.com/p/google-gson/issues/detail?id=231 и .Я только что опубликовал пример полиморфной десериализации с Gson в ответ на Полиморфизм с gson .

У Gson, надеюсь, скоро будет RuntimeTypeAdapter для более простой полиморфной десериализации.См. http://code.google.com/p/google-gson/issues/detail?id=231 для получения дополнительной информации.

С другой стороны, решение на основе Джексона не так уж и плохо.

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