Retrofit和GSON解析没有名字的对象数组

时间:2017-01-17 13:59:17

标签: android gson retrofit retrofit2

我得到了这样的JSON响应:

{
    "USA": [
        "New York",
        "Texas",
        "Hawaii"
    ],
    "Turkey": [
        "Ankara",
        "Istanbul"
    ],
    "Lebanon": [
        "Beirut",
        "Zahle"
    ]
}

我希望将其作为

public class Country {
    private String name = null;
    private List<String> cities = null;
}

如果JSON对象没有像

这样的名称,我们如何解析它们
{
    "name": "USA",
    "cities": [
              "New York",
              .... etc
}

?提前谢谢。

2 个答案:

答案 0 :(得分:4)

看起来像是我的地图。尝试将其解析为Map<String, List<String>>。然后,您可以单独访问键和值。

答案 1 :(得分:1)

Gson的默认DTO字段注释用于简单案例。对于更复杂的反序列化,您可能希望使用自定义类型适配器和(反)序列化器,以避免使用更加Gson惯用方式的弱类型DTO,如地图和列表。

假设您有以下DTO:

final class Country {

    private final String name;
    private final List<String> cities;

    Country(final String name, final List<String> cities) {
        this.name = name;
        this.cities = cities;
    }

    String getName() {
        return name;
    }

    List<String> getCities() {
        return cities;
    }

}

假设&#34;非标准&#34;在JSON布局中,以下反序列化器将递归遍历JSON对象树,以便收集目标国家/地区列表。说,

final class CountriesJsonDeserializer
        implements JsonDeserializer<List<Country>> {

    private static final JsonDeserializer<List<Country>> countryArrayJsonDeserializer = new CountriesJsonDeserializer();

    private static final Type listOfStringType = new TypeToken<List<String>>() {
    }.getType();

    private CountriesJsonDeserializer() {
    }

    static JsonDeserializer<List<Country>> getCountryArrayJsonDeserializer() {
        return countryArrayJsonDeserializer;
    }

    @Override
    public List<Country> deserialize(final JsonElement json, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final List<Country> countries = new ArrayList<>();
        final JsonObject root = json.getAsJsonObject();
        for ( final Entry<String, JsonElement> e : root.entrySet() ) {
            final String name = e.getKey();
            final List<String> cities = context.deserialize(e.getValue(), listOfStringType);
            countries.add(new Country(name, cities));
        }
        return countries;
    }

}

上面的反序列化器将绑定到List<Country>映射。作为第一步,它&#34;演员&#34;摘要JsonElementJsonObject以遍历其属性("USA""Turkey""Lebanon")。假设属性名称本身就是国家名称,城市名称列表(有效的属性值)可以更深入地委托给序列化上下文并解析为List<String>实例(注意类型标记)。解析namecities后,您可以构建Country实例并收集结果列表。

如何使用:

private static final Type listOfCountryType = new TypeToken<List<Country>>() {
}.getType();

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(listOfCountryType, getCountryArrayJsonDeserializer())
        .create();

public static void main(final String... args) {
    final List<Country> countries = gson.fromJson(JSON, listOfCountryType);
    for ( final Country country : countries ) {
        out.print(country.getName());
        out.print(" => ");
        out.println(country.getCities());
    }
}

已知类型标记和Gson实例是线程安全的,因此它们可以安全地存储为最终静态实例。请注意List<Country>的自定义类型和CountriesJsonDeserializer的自定义反序列化程序相互绑定的方式。一旦反序列化完成,它将输出:

  

USA =&gt; [纽约,德克萨斯州,夏威夷]
  土耳其=&gt; [安卡拉,伊斯坦布尔]
  黎巴嫩=&gt; [贝鲁特,扎赫尔]

更新

由于我从未使用过Retrofit,因此我使用以下配置尝试了以下代码:

  • com.google.code.gson:gson:2.8.0
  • com.squareup.retrofit2:retrofit:2.1.0
  • com.squareup.retrofit2:converter-gson:2.1.0

定义&#34; geo&#34;服务接口:

interface IGeoService {

    @GET("/countries")
    Call<List<Country>> getCountries();

}

使用自定义Gson感知转换器构建Retrofit实例:

// The statics are just borrowed from the example above

public static void main(final String... args) {
    // Build the Retrofit instance
    final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(... your URL goes here...)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();
    // Proxify the geo service by Retrofit
    final IGeoService geoService = retrofit.create(IGeoService.class);
    // Make a call to the remote service
    final Call<List<Country>> countriesCall = geoService.getCountries();
    countriesCall.enqueue(new Callback<List<Country>>() {
        @Override
        public void onResponse(final Call<List<Country>> call, final Response<List<Country>> response) {
            dumpCountries("From a remote JSON:", response.body());
        }

        @Override
        public void onFailure(final Call<List<Country>> call, final Throwable throwable) {
            throw new RuntimeException(throwable);
        }
    });
    // Or just take a pre-defined string
    dumpCountries("From a ready-to-use JSON:", gson.<List<Country>>fromJson(JSON, listOfCountryType));
}

private static void dumpCountries(final String name, final Iterable<Country> countries) {
    out.println(name);
    for ( final Country country : countries ) {
        out.print(country.getName());
        out.print(" => ");
        out.println(country.getCities());
    }
    out.println();
}

如果由于Country类及其JSON反序列化器而导致类型冲突(我的意思是,您已经有另一个&#34; country&#34;类用于不同目的),只需重命名这个 Country课程,以免影响&#34;良好的工作&#34;映射。

输出:

  

从即用型JSON:
  USA =&gt; [纽约,德克萨斯州,夏威夷]
  土耳其=&gt; [安卡拉,伊斯坦布尔]
  黎巴嫩=&gt; [贝鲁特,扎赫尔]

     

来自远程JSON:
  USA =&gt; [纽约,德克萨斯州,夏威夷]
  土耳其=&gt; [安卡拉,伊斯坦布尔]
  黎巴嫩=&gt; [贝鲁特,扎赫尔]