首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Moshi有一个像Gson一样的Runtime适配器工厂吗?

Moshi有一个像Gson一样的Runtime适配器工厂吗?
EN

Stack Overflow用户
提问于 2018-04-11 17:55:01
回答 1查看 1.7K关注 0票数 1

我已经使用Gson几周了,并且我发现了Runtime类,它允许“调整其运行时类型可能与声明类型不同的值”。

下面是我使用Gson的当前代码:

代码语言:javascript
复制
public class Database {
private final Gson gson;

private Database() {
    // Initialize Gson
    RuntimeTypeAdapterFactory<Base> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
            .of(Base.class, "table")
            .registerSubtype(AdminsTbl.class, "admins");
    this.gson = new GsonBuilder().registerTypeAdapterFactory(runtimeTypeAdapterFactory).create();
}
}

我的JSON中有一个"table“字段,它告诉Gson要使用哪个类(在本例中为"admins”-> AdminsTbl.class)。莫希有这样的东西吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-04-12 11:29:19

事实上,Gson并不在其标准包中提供RuntimeTypeAdapterFactory本身。快速搜索一个Moshi实现会让你的问题出现在搜索结果的顶端,至少对我来说是这样。)我在com.squareup.moshi.recipes包中也找不到类似的东西。但是,您可以轻松地自己实现它:

代码语言:javascript
复制
public final class MoshiRuntimeTypeJsonAdapterFactory
        implements JsonAdapter.Factory {

    private static final String DEFAULT_CLASS_NAME_PROPERTY = "type";

    private final Class<?> baseClass;
    private final String classNameProperty;
    private final Map<String, Class<?>> classNameToClass = new HashMap<>();
    private final Map<Class<?>, String> classToClassName = new HashMap<>();

    private MoshiRuntimeTypeJsonAdapterFactory(final Class<?> baseClass, final String classNameProperty) {
        this.baseClass = baseClass;
        this.classNameProperty = classNameProperty;
    }

    public static MoshiRuntimeTypeJsonAdapterFactory of(final Class<?> expectedClass) {
        return new MoshiRuntimeTypeJsonAdapterFactory(expectedClass, DEFAULT_CLASS_NAME_PROPERTY);
    }

    public static MoshiRuntimeTypeJsonAdapterFactory of(final Class<?> expectedClass, final String classNameProperty) {
        return new MoshiRuntimeTypeJsonAdapterFactory(expectedClass, classNameProperty);
    }

    public MoshiRuntimeTypeJsonAdapterFactory with(final Class<?> concreteClass) {
        return with(concreteClass, concreteClass.getSimpleName());
    }

    public MoshiRuntimeTypeJsonAdapterFactory with(final Class<?> concreteClass, final String className)
            throws IllegalArgumentException {
        if ( classNameToClass.containsKey(className) ) {
            throw new IllegalArgumentException(className + " is already registered for " + concreteClass);
        }
        if ( classToClassName.containsKey(concreteClass) ) {
            throw new IllegalArgumentException(concreteClass + " is already registered for " + className);
        }
        classNameToClass.put(className, concreteClass);
        classToClassName.put(concreteClass, className);
        return this;
    }

    @Nullable
    @Override
    public JsonAdapter<?> create(final Type type, final Set<? extends Annotation> annotations, final Moshi moshi) {
        if ( !(type instanceof Class) ) {
            return null;
        }
        final Class<?> typeAsClass = (Class<?>) type;
        if ( !baseClass.isAssignableFrom(typeAsClass) ) {
            return null;
        }
        final JsonAdapter<Object> jsonObjectJsonAdapter = moshi.nextAdapter(this, Map.class, ImmutableSet.of());
        final LoadingCache<Class<?>, JsonAdapter<Object>> jsonAdaptersCache = CacheBuilder.newBuilder()
                .build(new CacheLoader<Class<?>, JsonAdapter<Object>>() {
                    @Override
                    public JsonAdapter<Object> load(final Class<?> clazz) {
                        return moshi.nextAdapter(MoshiRuntimeTypeJsonAdapterFactory.this, clazz, ImmutableSet.copyOf(clazz.getAnnotations()));
                    }
                });
        return new JsonAdapter<Object>() {
            @Nullable
            @Override
            public Object fromJson(final JsonReader jsonReader)
                    throws IOException {
                try {
                    @SuppressWarnings("unchecked")
                    final Map<String, Object> jsonObject = (Map<String, Object>) jsonReader.readJsonValue();
                    assert jsonObject != null;
                    final Object rawClassName = jsonObject.get(classNameProperty);
                    if ( !(rawClassName instanceof String) ) {
                        throw new IOException("Type name: expected a string in " + classNameProperty + ", but got " + rawClassName);
                    }
                    final String className = (String) rawClassName;
                    final Class<?> concreteClass = classNameToClass.get(className);
                    if ( concreteClass == null ) {
                        throw new IOException("No mapping registered for " + className);
                    }
                    final JsonAdapter<Object> jsonAdapter = jsonAdaptersCache.get(concreteClass);
                    return jsonAdapter.fromJsonValue(jsonObject);
                } catch ( final ExecutionException ex ) {
                    throw new RuntimeException(ex);
                }
            }

            @Override
            public void toJson(final JsonWriter jsonWriter, @Nullable final Object value)
                    throws IOException {
                try {
                    assert value != null;
                    final Class<?> concreteClass = value.getClass();
                    final String className = classToClassName.get(concreteClass);
                    if ( className == null ) {
                        throw new IOException("No mapping registered for " + concreteClass);
                    }
                    final JsonAdapter<Object> valueJsonAdapter = jsonAdaptersCache.get(concreteClass);
                    @SuppressWarnings("unchecked")
                    final Map<String, Object> jsonObject = (Map<String, Object>) valueJsonAdapter.toJsonValue(value);
                    assert jsonObject != null;
                    jsonObject.put(classNameProperty, className);
                    jsonObjectJsonAdapter.toJson(jsonWriter, jsonObject);
                } catch ( final ExecutionException ex ) {
                    throw new RuntimeException(ex);
                }
            }
        };
    }

}

此实现将Google用于不可变集合(ImmutableSet)和缓存(LoadingCache),但您可以轻松地自行替换它们。我还相信,对于潜在的与Moshi相关的性能问题,这种实现也可以得到改进。

来自RuntimeTypeAdapterFactory的简单示例是这里适配:

代码语言:javascript
复制
private static final Moshi moshi = new Moshi.Builder()
        .add(MoshiRuntimeTypeJsonAdapterFactory.of(Shape.class)
                .with(Shape.Circle.class)
                .with(Shape.Diamond.class)
                .with(Shape.Rectangle.class)
        )
        .build();
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49781604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档