【问题标题】:How to get inner JSON object with GSON/Retrofit when outer name is variable?当外部名称可变时,如何使用 GSON/Retrofit 获取内部 JSON 对象?
【发布时间】:2017-07-06 08:26:37
【问题描述】:

我有一个来自 API 的 JSON 响应,如下所示:

{"asalas77":
    {"id":23519033,"name":"Asalas77","profileIconId":22,"revisionDate":1487214366000,"summonerLevel":30}
}

我需要从中提取内部对象。我尝试使用这个问题Get nested JSON object with GSON using retrofit 中所示的解串器,但它对我不起作用。

public class SummonerDeserializer implements JsonDeserializer<Summoner> {
@Override
public Summoner deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
        throws JsonParseException {
    long id = je.getAsJsonObject().get("id").getAsLong();
    String name = je.getAsJsonObject().get("name").getAsString();
    int profileIconId = je.getAsJsonObject().get("profileIconId").getAsInt();
    long revisionDate = je.getAsJsonObject().get("revisionDate").getAsLong();
    long summonerLevel = je.getAsJsonObject().get("summonerLevel").getAsLong();
    Summoner s = new Summoner();
    s.setId(id);
    s.setName(name);
    s.setProfileIconId(profileIconId);
    s.setRevisionDate(revisionDate);
    s.setSummonerLevel(summonerLevel);

    return s;
}
}

但问题是我无法从 JsonElement je 访问内部字段,而名称 asalas77 是一个变量(它是一个搜索查询),所以我无法直接提取内部对象。

【问题讨论】:

    标签: json serialization gson retrofit


    【解决方案1】:

    您必须有一个包装类,以免与反序列化策略发生冲突。假设如下:

    final class SummonerResponse {
    
        private final Summoner summoner;
    
        private SummonerResponse(final Summoner summoner) {
            this.summoner = summoner;
        }
    
        static SummonerResponse summonerResponse(final Summoner summoner) {
            return new SummonerResponse(summoner);
        }
    
        Summoner getSummoner() {
            return summoner;
        }
    
    }
    

    然后您可以创建自定义响应反序列化器:

    final class SummonerWrapperDeserializer
            implements JsonDeserializer<SummonerResponse> {
    
        private static final JsonDeserializer<SummonerResponse> summonerDeserializer = new SummonerWrapperDeserializer();
    
        private SummonerWrapperDeserializer() {
        }
    
        static JsonDeserializer<SummonerResponse> getSummonerResponseDeserializer() {
            return summonerDeserializer;
        }
    
        @Override
        public SummonerResponse deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
                throws JsonParseException {
            // Pick the root as a JSON object
            final JsonObject outerJsonObject = jsonElement.getAsJsonObject();
            // And check how many properties does it have
            final Iterable<? extends Entry<String, JsonElement>> outerJsonObjectEntries = outerJsonObject.entrySet();
            if ( outerJsonObject.size() != 1 ) {
                throw new JsonParseException("Expected one property object, the actual properties are: " + getPropertyName(outerJsonObjectEntries));
            }
            // If it has only one property, just get the property and take its inner value
            final JsonElement innerJsonElement = outerJsonObjectEntries.iterator().next().getValue();
            // Once it's obtained, just delegate the parsing to a downstream parser - no need to create Summoner instances by hands
            return summonerResponse(context.deserialize(innerJsonElement, Summoner.class));
        }
    
        private static Set<String> getPropertyName(final Iterable<? extends Entry<String, JsonElement>> entries) {
            final Set<String> keys = new LinkedHashSet<>();
            for ( final Entry<String, JsonElement> entry : entries ) {
                keys.add(entry.getKey());
            }
            return keys;
        }
    
    }
    

    或者节省一些内存(JSON(反)序列化程序需要一些内存,因为它们使用 JSON 树)并创建一个更底层的类型适配器:

    final class SummonerResponseTypeAdapterFactory
            implements TypeAdapterFactory {
    
        private static final TypeAdapterFactory summonerResponseTypeAdapterFactory = new SummonerResponseTypeAdapterFactory();
    
        private SummonerResponseTypeAdapterFactory() {
        }
    
        static TypeAdapterFactory getSummonerResponseTypeAdapterFactory() {
            return summonerResponseTypeAdapterFactory;
        }
    
        @Override
        public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
            // Check if we can handle SummonerResponse. Classes can be compared with `==`
            if ( typeToken.getRawType() == SummonerResponse.class ) {
                final TypeAdapter<SummonerResponse> typeAdapter = getSummonerResponseTypeAdapter(gson);
                @SuppressWarnings("unchecked")
                final TypeAdapter<T> castTypeAdapter = (TypeAdapter<T>) typeAdapter;
                return castTypeAdapter;
            }
            return null;
        }
    
    }
    
    final class SummonerResponseTypeAdapter
            extends TypeAdapter<SummonerResponse> {
    
        private final Gson gson;
    
        private SummonerResponseTypeAdapter(final Gson gson) {
            this.gson = gson;
        }
    
        static TypeAdapter<SummonerResponse> getSummonerResponseTypeAdapter(final Gson gson) {
            return new SummonerResponseTypeAdapter(gson);
        }
    
        @Override
        @SuppressWarnings("resource")
        public void write(final JsonWriter out, final SummonerResponse summonerResponse)
                throws IOException {
            // The incoming object may be null
            if ( summonerResponse == null && gson.serializeNulls() ) {
                out.nullValue();
                return;
            }
            // Generate the inner object
            out.beginObject();
            out.name(summonerResponse.getSummoner().name);
            gson.toJson(summonerResponse.getSummoner(), Summoner.class, out);
            out.endObject();
        }
    
        @Override
        public SummonerResponse read(final JsonReader in)
                throws IOException {
            // is it a null?
            if ( in.peek() == NULL ) {
                return null;
            }
            // make sure that the inner read JSON contains an inner object
            in.beginObject();
            // ignore the name
            in.nextName();
            // delegate parsing to the backing Gson instance in order to apply downstream parsing
            final Summoner summoner = gson.fromJson(in, Summoner.class);
            // check if there are more properties within the inner object
            if ( in.peek() == NAME ) {
                throw new MalformedJsonException("Unexpected: " + in.nextName());
            }
            // consume the "}" token
            in.endObject();
            return summonerResponse(summoner);
        }
    
    }
    

    那么上面的任何一个选项都可以这样使用:

    final Gson gson = new GsonBuilder()
            .registerTypeAdapter(SummonerResponse.class, getSummonerResponseDeserializer())
            .create();
    final SummonerResponse summonerResponse = gson.fromJson(JSON, SummonerResponse.class);
    final Summoner summoner = summonerResponse.getSummoner();
    out.println(summoner.id + " => " + summoner.name);
    

    final Gson gson = new GsonBuilder()
            .registerTypeAdapterFactory(getSummonerResponseTypeAdapterFactory())
            .create();
    final SummonerResponse summonerResponse = gson.fromJson(JSON, SummonerResponse.class);
    final Summoner summoner = summonerResponse.getSummoner();
    out.println(summoner.id + " => " + summoner.name);
    out.println(gson.toJson(summonerResponse));
    

    输出是

    23519033 => 阿萨拉斯77

    23519033 => 阿萨拉斯77
    {"Asalas77":{"id":23519033,"name":"Asalas77","profileIconId":22,"revisionDate":1487214366000,"summonerLevel":30}}

    分别。

    【讨论】:

      猜你喜欢
      • 2016-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 1970-01-01
      相关资源
      最近更新 更多