#java #android #json #gson
#java #Android #json #gson
Вопрос:
У меня есть этот класс
Myclass
{
transient String field1;
transient String field2;
... // other non transient fields
}
Я храню сериализованные объекты, использую их через сеть таким образом (временные поля исключены).
Однако, только для одного конкретного случая, мне нужно включить field2 в сериализацию.
Есть ли способ не исключать определенное временное поле при сериализации с использованием gson?
Ответ №1:
Решение 0:
Используйте пользовательский адаптер типа для класса.
Учитывая
@JsonAdapter(KA.class)
class K{
private transient String name;
private transient String password;
}
class Entity_Adapter extends TypeAdapter<Entity>{
@Override
public void write(JsonWriter out, Entity value) throws IOException {
out.beginObject();
out.name("name");
out.value(value.getName());
out.name("password");
out.value(value.getPassword());
out.endObject();
}
@Override
public Entity read(JsonReader in) throws IOException {
Entity k=new Entity();
in.beginObject();
in.nextName();
k.setName(in.nextString());
in.nextName();
k.setPassword(in.nextString());
in.endObject();
return k;
}
}
полный пример здесь
Решение 1: (не надежное)
Добавьте другое не transient
поле и всегда копируйте любое новое установленное значение field2
для него. например
transient String field1;
transient String field2;
@SerializedName("field2")
private String field2_non_trans;
public void setField2(String arg_val){
this.field2 = arg_val;
this.field2_non_trans = arg_val;
}
public String getField2(){
if(field2 == null){
field2 = field2_non_trans;
}
return field2;
}
полный пример здесь
Но вы ДОЛЖНЫ отслеживать каждое изменение в этом field2
, чтобы всегда сохранять копию значения для field2_non_trans
обновления, поэтому, если это field2
задано конструктором или вне его setter
функции, вы должны быть уверены, что вы установили значение copy для field2_non_trans
То же самое для десериализации, вы должны либо:
- после завершения десериализации вам необходимо установить десериализованное значение
field2_non_trans
tofield2
с помощью метода. - Или просто вернуть
field2_non_trans
getField2()
методом, гдеfield2
null
Решение 2:
Отметьте, что field2
оно не является переходным.
Комментарии:
1. Решения 1 и 2 создадут поле ‘field2’ для других сериализаций, что в моем случае не подходит. Но адаптер типа, sol. 0, выглядит многообещающе, я рассмотрю его, когда у меня будет такая возможность. Спасибо.
2. Написание пользовательского адаптера технически решает эту проблему, но это утомительная работа, когда класс большой, и его сложно написать так, чтобы его было легко поддерживать. 1 за идею.
Ответ №2:
Вы можете дублировать свое временное поле другим не переходным и записать значение из установщика временных полей. Идея заключается в обновлении поля клонирования при каждом обновлении временного поля.
Комментарии:
1. Да, но разве это не создаст поле ‘field2’ для других сериализаций? В моем случае это не подходит. Я думаю, мне нужен smt как пользовательский конструктор для этого конкретного случая
Ответ №3:
Несмотря на то, что я бы никогда не рекомендовал использовать один и тот же класс для разных библиотек из-за подобных проблем, вы можете легко управлять тем, как Gson применяет стратегии исключения к сериализуемым и десериализуемым полям.
public final class TransientExclusionStrategy
implements ExclusionStrategy {
private static final ExclusionStrategy instance = new TransientExclusionStrategy();
private TransientExclusionStrategy() {
}
public static ExclusionStrategy getInstance() {
return instance;
}
@Override
public boolean shouldSkipField(final FieldAttributes attributes) {
@Nullable
final Expose expose = attributes.getAnnotation(Expose.class);
if ( expose == null ) {
return attributes.hasModifier(Modifier.TRANSIENT);
}
return !expose.serialize();
}
@Override
public boolean shouldSkipClass(final Class<?> clazz) {
return false;
}
}
Эта реализация прошла бы следующий модульный тест:
public final class TransientExclusionStrategyTest {
private static final String EXPOSED = "EXPOSED";
private static final String IGNORED = "IGNORED";
@SuppressWarnings("all")
private static final class MyClass {
final String s0 = EXPOSED; // no explicit expose, serialized by default
@Expose(serialize = false)
final String s1 = IGNORED; // ignored by annotation
@Expose(serialize = true)
final String s2 = EXPOSED; // serialized by annotation
final transient String ts0 = IGNORED; // no explicit expose, ignored by default
@Expose(serialize = false)
final transient String ts1 = IGNORED; // ignored by annotation
@Expose(serialize = true)
final transient String ts2 = EXPOSED; // serialized by annotation
}
@Test
public void test() {
final Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(TransientExclusionStrategy.getInstance())
.create();
final JsonObject json = (JsonObject) gson.toJsonTree(new MyClass());
for ( final Map.Entry<String, JsonElement> e : json.entrySet() ) {
final String stringValue = e.getValue().getAsString();
Assertions.assertEquals(EXPOSED, stringValue, () -> "Expected " EXPOSED " but was " stringValue " for " e.getKey());
}
}
}
Таким образом, вам не нужно обрабатывать какие-либо специальные адаптеры типов для каждого такого «специального» класса или вводить промежуточные поля (это не обязательно не конфликтует с другими библиотеками и фреймворками, которые вы используете).