You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解析内部元素无键的嵌套List JSON?Java对象结构配置

解决JSON嵌套数组的Gson解析问题

问题分析

你遇到两个核心问题:

  1. 根节点解析错误:报错Expected BEGIN_ARRAY but was BEGIN_OBJECT,说明Retrofit接口的返回类型定义错误——API实际返回单个JSON对象,但你可能定义成了Call<List<QueryResult>>或Observable<List<QueryResult>>。
  2. 嵌套数组映射错误:API返回的prices和market_caps是无键数组的数组,而非对象数组,你定义的List<List<Prices>>无法让Gson自动将无键数组映射到带字段的Prices类。

正确的解决方案

步骤1:修正Retrofit接口的返回类型

确保接口返回单个QueryResult对象,而非列表。示例:

public interface PriceApi {
    @GET("your-target-endpoint")
    Observable<QueryResult> getPriceData();
    // 若不用RxJava,可改为:Call<QueryResult> getPriceData();
}

步骤2:调整实体类结构并添加自定义TypeAdapter

由于JSON中的每个价格/市值条目是无键数组([时间戳, 数值]),需要自定义Gson的TypeAdapter来完成数组到实体类的映射:

1. 定义基础实体类

public class PriceEntry {
    private long timestamp;
    private double price;

    public PriceEntry(long timestamp, double price) {
        this.timestamp = timestamp;
        this.price = price;
    }

    // Getters
    public long getTimestamp() { return timestamp; }
    public double getPrice() { return price; }
}

public class MarketCapEntry {
    private long timestamp;
    private double marketCap;

    public MarketCapEntry(long timestamp, double marketCap) {
        this.timestamp = timestamp;
        this.marketCap = marketCap;
    }

    // Getters
    public long getTimestamp() { return timestamp; }
    public double getMarketCap() { return marketCap; }
}

2. 编写自定义TypeAdapter

为PriceEntry和MarketCapEntry实现TypeAdapter,处理无键数组的解析:

public class PriceEntryTypeAdapter extends TypeAdapter<PriceEntry> {
    @Override
    public void write(JsonWriter out, PriceEntry value) throws IOException {
        // 若无需序列化可留空,或按需求实现序列化逻辑
        out.beginArray();
        out.value(value.getTimestamp());
        out.value(value.getPrice());
        out.endArray();
    }

    @Override
    public PriceEntry read(JsonReader in) throws IOException {
        in.beginArray();
        long timestamp = in.nextLong();
        double price = in.nextDouble();
        in.endArray();
        return new PriceEntry(timestamp, price);
    }
}

public class MarketCapEntryTypeAdapter extends TypeAdapter<MarketCapEntry> {
    @Override
    public void write(JsonWriter out, MarketCapEntry value) throws IOException {
        out.beginArray();
        out.value(value.getTimestamp());
        out.value(value.getMarketCap());
        out.endArray();
    }

    @Override
    public MarketCapEntry read(JsonReader in) throws IOException {
        in.beginArray();
        long timestamp = in.nextLong();
        double marketCap = in.nextDouble();
        in.endArray();
        return new MarketCapEntry(timestamp, marketCap);
    }
}

3. 调整QueryResult类并关联TypeAdapter

public class QueryResult {
    @SerializedName("prices")
    @JsonAdapter(PriceEntryTypeAdapter.class)
    private List<PriceEntry> prices;

    @SerializedName("market_caps")
    @JsonAdapter(MarketCapEntryTypeAdapter.class)
    private List<MarketCapEntry> marketCaps;

    // Getters
    public List<PriceEntry> getPrices() { return prices; }
    public List<MarketCapEntry> getMarketCaps() { return marketCaps; }
}

步骤3:全局注册TypeAdapter(可选)

如果不想在实体类上使用@JsonAdapter注解,可以在创建Gson实例时全局注册TypeAdapter:

@Provides
@Singleton
public Retrofit provideRetrofit(OkHttpClient okHttpClient) {
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(PriceEntry.class, new PriceEntryTypeAdapter())
            .registerTypeAdapter(MarketCapEntry.class, new MarketCapEntryTypeAdapter())
            .create();

    return new Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
            .client(okHttpClient)
            .build();
}

简化方案(无需自定义TypeAdapter)

如果不需要实体类,可直接用List<List<Double>>接收数据,使用时手动转换类型:

public class QueryResult {
    @SerializedName("prices")
    private List<List<Double>> prices;

    @SerializedName("market_caps")
    private List<List<Double>> marketCaps;

    // Getters
    public List<List<Double>> getPrices() { return prices; }
    public List<List<Double>> getMarketCaps() { return marketCaps; }
}

转换示例:

long timestamp = prices.get(0).get(0).longValue();
double price = prices.get(0).get(1);

内容的提问来源于stack exchange,提问作者WHOATEMYNOODLES

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 18:24:29