当对象是另一个对象的字段时,为什么GSON无法转换对象?[duplicate]

daolsyd0  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(194)

此问题在此处已有答案

Gson doesn't serialize fields defined in subclasses(4个答案)
去年关闭了。
Errorneous位于其他Object内部时,GSON无法正确地将Errorneous转换为JSON。
但是当它被转换为顶级对象时,它工作得很好。为什么,如何修复它?
示例:

import com.google.gson.GsonBuilder

sealed class Errorneous<R> {}
data class Success<R>(val result: R) : Errorneous<R>()
data class Fail<R>(val error: String) : Errorneous<R>()

class Container(val value: Errorneous<String>)

fun main() {
  print(GsonBuilder().create().toJson(Container(Fail("some error"))))

  print(GsonBuilder().create().toJson(Fail<String>("some error")))
}

输出量

{"value":{}}

{"error":"some error"}

但应该是

{"value":{"error":"some error"}}

{"error":"some error"}
oknrviil

oknrviil1#

我在帖子下面对Gson的行为做了一些评论(简而言之:没有足够运行时类型信息),因此这只是使其工作并使其实际具有类型感知的代码。

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new TypeAdapterFactory() {
            @Override
            @Nullable
            public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
                final Class<? super T> rawType = typeToken.getRawType();
                if ( rawType != Errorneous.class ) {
                    return null;
                }
                final ParameterizedType parameterizedType = (ParameterizedType) typeToken.getType();
                @SuppressWarnings("unchecked")
                final TypeToken<Success<?>> successTypeToken = (TypeToken<Success<?>>) TypeToken.getParameterized(Success.class, parameterizedType.getActualTypeArguments());
                @SuppressWarnings("unchecked")
                final TypeToken<Fail<?>> failTypeToken = (TypeToken<Fail<?>>) TypeToken.getParameterized(Fail.class, parameterizedType.getActualTypeArguments());
                final TypeAdapter<Success<?>> successTypeAdapter = gson.getDelegateAdapter(this, successTypeToken);
                final TypeAdapter<Fail<?>> failTypeAdapter = gson.getDelegateAdapter(this, failTypeToken);
                final TypeAdapter<Errorneous<?>> concreteTypeAdapter = new TypeAdapter<Errorneous<?>>() {
                    @Override
                    public void write(final JsonWriter out, final Errorneous<?> value)
                            throws IOException {
                        if ( value instanceof Success ) {
                            final Success<?> success = (Success<?>) value;
                            successTypeAdapter.write(out, success);
                            return;
                        }
                        if ( value instanceof Fail ) {
                            final Fail<?> fail = (Fail<?>) value;
                            failTypeAdapter.write(out, fail);
                            return;
                        }
                        throw new AssertionError(); // even null cannot get here: it is protected with .nullSafe() below
                    }

                    @Override
                    public Errorneous<?> read(final JsonReader in) {
                        throw new UnsupportedOperationException();
                    }
                };
                @SuppressWarnings("unchecked")
                final TypeAdapter<T> typeAdapter = ((TypeAdapter<T>) concreteTypeAdapter)
                        .nullSafe();
                return typeAdapter;
            }
        })
        .create();

@AllArgsConstructor
@SuppressWarnings("unused")
private abstract static class Errorneous<R> {
}

@AllArgsConstructor
@SuppressWarnings("unused")
private static final class Success<R>
        extends Errorneous<R> {

    private final R result;

}

@AllArgsConstructor
@SuppressWarnings("unused")
private static final class Fail<R>
        extends Errorneous<R> {

    private final String error;

}

@AllArgsConstructor
@SuppressWarnings("unused")
private static class Container {

    private final Errorneous<String> value;

}

public static void main(final String... args) {
    System.out.println(gson.toJson(new Container(new Fail<>("some error"))));
    System.out.println(gson.toJson(new Fail<>("some error")));
}

正如您所看到的,类型适配器工厂首先解析SuccessFail的类型适配器,然后使用instanceof()基于Errorneous值的实际类选择一个合适的类型适配器。
以下是它打印的内容:

{"value":{"error":"some error"}}
{"error":"some error"}

由于反序列化必须决定JSON的反序列化方式,因此该操作不受支持:1)或者在类型指示符字段上(参见Gson extras中的RuntimeTypeAdapterFactory,其在GitHub上的存储库中;它没有作为工件捆绑和发布); 2)或分析对象的结构,进行启发式分析(更难实现,并且可能面临不明确的情况)。
我不使用Kotlin,但是上面的Java代码可能可以很容易地在IntelliJ IDEA中转换成Kotlin对应代码。

06odsfpq

06odsfpq2#

Kotlin中的答案复制自类似的Java问题

import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type

sealed class Errorneous<R> {}
data class Success<R>(val result: R) : Errorneous<R>()
data class Fail<R>(val error: String) : Errorneous<R>()

class Container(
  val value: Errorneous<String>
 )

fun main() {
  val builder = GsonBuilder()
  builder.registerTypeAdapter(
    Errorneous::class.java, ErrorneousSerializer()
  )
  val gson = builder.create()
  print(gson.toJson(Container(Fail("some error"))))
  print(gson.toJson(Fail<String>("some error")))
}

class ErrorneousSerializer : JsonSerializer<Errorneous<Any>> {
  override fun serialize(
    o: Errorneous<Any>, type: Type, ctx: JsonSerializationContext
  ): JsonElement {
    return ctx.serialize(o as Any)
  }
}

相关问题