GSON создать один объект из сложного объекта - PullRequest
1 голос
/ 02 марта 2012

Представьте, что у нас есть следующие 2 класса:

public class Test {
    private String element1;
    private SubTest subTest;
}
public class SubTest {
    private String element2;
}

Если я создам json из класса Test, у меня будет следующая строка:

{element1:null,subTest:{element2:null}}

Но мне нужен результат jsonс этим видом:

{element1:null,subTest_element2:null}

Я знаю, что могу сделать это, создав адаптер, в котором я могу реализовать метод сериализации, как мне хочется, но мне нужно что-то еще, например, аннотацию, которую я помещу под private SubTest subTest;

Может ли кто-нибудь помочь мне с этим?

1 Ответ

2 голосов
/ 03 марта 2012

Новый интерфейс Gson 2.2 TypeAdapterFactory дает вам возможность проверять аннотации входящего типа и определять адаптер типа на основе этих аннотаций. Вот полный пример, который ищет поле с комментариями JsonInlined. Если он найден, адаптер типа будет сериализовать внешний объект как внутренний объект.

@Retention(RetentionPolicy.RUNTIME)
@interface JsonInlined {}

static class InlinedFieldFactory implements TypeAdapterFactory {
  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
    Class<? super T> rawType = type.getRawType();
    Field field = getJsonInlinedField(rawType);
    if (field == null) {
      return null; // this factory doesn't know how to adapt this type
    }
    field.setAccessible(true);
    TypeAdapter<?> delegate = gson.getAdapter(field.getType());
    @SuppressWarnings("unchecked") // this creates a type adapter handles for instances of 'T'
    TypeAdapter<T> fieldAdapter = (TypeAdapter<T>) newAdapter(rawType, field, delegate);
    return fieldAdapter;
  }

  private Field getJsonInlinedField(Class<?> c) {
    for (Field field : c.getDeclaredFields()) {
      if (field.isAnnotationPresent(JsonInlined.class)) {
        return field;
      }
    }
    return null;
  }

  static <F> TypeAdapter<Object> newAdapter(final Class<?> c,
      final Field field, final TypeAdapter<F> fieldAdapter) {
    return new TypeAdapter<Object>() {
      @Override public void write(JsonWriter out, Object value) throws IOException {
        try {
          if (value != null) {
            @SuppressWarnings("unchecked") // we define 'F' by the type of field
            F fieldValue = (F) field.get(value);
            fieldAdapter.write(out, fieldValue);
          } else {
            out.nullValue();
          }
        } catch (IllegalAccessException e) {
          throw new AssertionError(e);
        }
      }
      @Override public Object read(JsonReader in) throws IOException {
        try {
          if (in.peek() == JsonToken.NULL) {
            return null;
          } else {
            Object instance = c.newInstance();
            field.set(instance, fieldAdapter.read(in));
            return instance;
          }
        } catch (InstantiationException e) {
          throw new AssertionError(e);
        } catch (IllegalAccessException e) {
          throw new AssertionError(e);
        }
      }
    };
  }
}

Я не собираюсь объяснять всю реализацию; в TypeAdapter и TypeAdapterFactory javadocs есть приличное описание движущихся частей.

Самое важное, что нужно помнить, это то, что вы можете составлять адаптеры типа с адаптерами другого типа. Заводской API заставляет вас делать все ваши размышления заранее. Это помогает отлавливать ошибки раньше, а также помогает вашему коду работать более эффективно.

...