【问题标题】:GSON: JSON deserialization to variable type (List/String)GSON:JSON 反序列化为变量类型(列表/字符串)
【发布时间】:2019-08-05 15:54:33
【问题描述】:

在这一点上,这已经是一个老问题了,我可能已经阅读了关于 SO 的所有相关主题。

但切中要害。也许我需要一些建议或更正?

出于某种原因,我们有 2 种类型的可生成 Json:

{"data": {"id": "value"}}{"data":[{"id": "value"}]}

对象和数组。还有其他参数,但它们在这里无关紧要。每个请求的“id”都不同。有时是 userId、portfolioId 等。所以我得到“id”并将其传递给相关的 var。

很长一段时间我都在处理第一个案例。并像这样创建 POJO:

Data.class

public class Data {

@SerializedName("id")
@Expose
private String id;

public Data() {
}

public Data(String id) {
    super();
    this.id = id;
}

protected String getId() {
    return id;
}

我通过 User.class 处理“数据”参数。

@JsonAdapter(UserDeserializer.class)
public Data data;


public Data getData() {
    return data;
}

public void setData(Data data) {
    this.data = data;
}


public User() {
}

public User(Data data) {
    super();
    this.data = data;
}

Gson gson = new Gson();

public String getPortfolioList(String tokenId, String userId) {
    Call<User> call = apiRequest.getPortfolioList(userId, tokenId);

    try {
        User newResult = gson.fromJson(String.valueOf(call.execute().body()), User.class);
        System.out.println(newResult.getData().getId());
    } catch (IOException e) {
        e.printStackTrace();
    }
    return getPortfolioId();
}

Deserializer.class

 public class UserDeserializer implements JsonDeserializer<User> {

    private Type listType = new TypeToken<List<Data>>(){}.getType();

    @Override
    public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {

        User user = new User();
        JsonElement jsonElement;
        if (json.isJsonArray()) {
            jsonElement = json.getAsJsonArray();
            user.data = context.deserialize(jsonElement,listType);
//            user.data = new Gson().fromJson(jsonElement, new TypeToken<List<Data>>() {}.getType());
        } else {
            jsonElement = json.getAsJsonObject();
            user.data = context.deserialize(jsonElement, Data.class);
//            user.setData(new Gson().fromJson(jsonElement, new TypeToken<Data>() {}.getType()));
        }
        return user;
    }
}

Gson builder 在 BaseApi 类中以防万一:

Gson gson = new GsonBuilder().registerTypeAdapter(UserDeserializer.class, new UserDeserializer()).setLenient().create();

如果没有自定义反序列化和数组 JSON 问题,这将完美运行。但现在我必须确定我得到的 "data" 的确切类型。

在上述情况下,我得到java.lang.ClassCastException: java.util.ArrayList cannot be cast to auto.Rest.Data

我假设我必须创建另一个 Data 类(例如会有 DataObjectDataArray)并像我一样描述每个参数在 Data.class 之前完成这项工作?我认为我在反序列化期间做错了,但我不确定在哪里。

或者我错了,可以调用 Data 作为 ListData 作为 Object同一个班?

我已经为此工作了几天(?)并且正在考虑使用泛型而不是 Gson 帮助,是的,我很绝望。因此,任何帮助表示赞赏。

【问题讨论】:

  • {"data":[{"id": "value"}]} 表示您可以拥有多个 id 的数组。在这种情况下,您的 Data 实例会是什么样子,例如为{"data":[{"id": "1"}, {"id": "2"}]}?
  • user.data = context.deserialize(jsonElement,listType); 无法工作,因为您正在反序列化 List&lt;Data&gt; 并试图将其分配给 Data 类型的字段。在这种情况下,您应该首先执行List&lt;Data&gt; dataList = context.deserialize(jsonElement,listType); 之类的操作,然后从该列表中获取其中一个元素(如果有的话)并将其分配给user.data
  • @Thomas 这是一个像这样的投资组合列表:{"data": [{"id":"c87ca3fe85781007869b83f", "template": 0, "publish": false, "publication_at": null, "likes": 0, "liked": false, "comments": 0, "cells": { "data": [ {/*someting else*/}]}}, {/*another portfolio like the same above*/}]} 我真的希望这是可以理解的。
  • @Thomas 谢谢。我会试试这个。我可以先将其分配给user.data,然后再分配给后者的.getId()吗?或者我必须为List&lt;Data&gt; 创建相同的方法?
  • 如果 Data 可以使用开箱即用的映射反序列化,那么只需将数组反序列化为列表并从反序列化的元素中获取 id 就足够了。

标签: java json gson deserialization retrofit


【解决方案1】:

如果您总是有objectone-element array,您可以编写自定义反序列化器,如下所示:

class OneOrElementJsonDeserializer<T> implements JsonDeserializer<T> {

    @Override
    public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        if (json instanceof JsonArray) {
            final JsonArray array = (JsonArray) json;
            final int size = array.size();
            if (size == 0) {
                return null;
            }

            return context.deserialize(array.get(0), typeOfT);
        }

        return context.deserialize(json, typeOfT);
    }
}

简化后的示例模型如下所示:

class User {

    @JsonAdapter(OneOrElementJsonDeserializer.class)
    private Data data;

    public User() {
    }

    public User(Data data) {
        super();
        this.data = data;
    }

    public Data getData() {
        return data;
    }

    public void setData(Data data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "User{" +
                "data=" + data +
                '}';
    }
}

class Data {

    private String id;

    protected String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Data{" +
                "id='" + id + '\'' +
                '}';
    }
}

示例用法:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.JsonAdapter;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Type;

public class GsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .create();

        User root = gson.fromJson(new FileReader(jsonFile), User.class);
        System.out.println(root);
    }
}

JSON 有效载荷的上方代码:

{
  "data": [
    {
      "id": "c87ca3fe85781007869b83f"
    }
  ]
}

打印:

User{data=Data{id='c87ca3fe85781007869b83f'}}

对于object 情况JSON 有效载荷:

{
  "data": {
    "id": "c87ca3fe85781007869b83f"
  }
}

打印:

User{data=Data{id='c87ca3fe85781007869b83f'}}

如果您的属性可能包含JSON objectmulti-element array,请参阅我对这个问题的回答Mapping Json Array to Java Models。有实现的反序列化器可以处理这样的情况。

【讨论】:

  • 感谢您的回答。对于 call 这工作得很好:User responseBody = call.execute().body(); System.out.println(responseBody.getData().getId());(是的,在我的情况下不需要异步调用)由于某种原因不需要gson.fromJson(); 这可能比我试图实现的更微妙的解决方案在 OP 中。
【解决方案2】:

如果总是有一个对象,只需添加

json.getAsJsonArray().get(0);

public class UserDeserializer implements JsonDeserializer<User> {

  private Type listType = new TypeToken<List<Data>>(){}.getType();

  @Override
  public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {

    User user = new User();
    JsonElement jsonElement;
    if (json.isJsonArray()) {
      jsonElement = json.getAsJsonArray().get(0);
      user.data = context.deserialize(jsonElement,listType);
//            user.data = new Gson().fromJson(jsonElement, new TypeToken<List<Data>>() {}.getType());
    } else {
      jsonElement = json.getAsJsonObject();
      user.data = context.deserialize(jsonElement, Data.class);
//            user.setData(new Gson().fromJson(jsonElement, new TypeToken<Data>() {}.getType()));
    }
    return user;
  }

}

如果有更多对象,将字段数据更改为列表

public class UserDeserializer implements JsonDeserializer<User> {

  private Type listType = new TypeToken<List<Data>>(){}.getType();

  @Override
  public User deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {

    User user = new User();
    JsonElement jsonElement;
    if (json.isJsonArray()) {
      jsonElement = json.getAsJsonArray();
      user.data = context.deserialize(jsonElement,listType);
//            user.data = new Gson().fromJson(jsonElement, new TypeToken<List<Data>>() {}.getType());
    } else {
      jsonElement = json.getAsJsonObject();
      List<Data> data = new ArrayList<Data>();
      data.add(context.deserialize(jsonElement, Data.class)) ;
      user.data = data ;
//            user.setData(new Gson().fromJson(jsonElement, new TypeToken<Data>() {}.getType()));
    }
    return user;
  }

}

并将 User.class 字段数据更改为 List

public List<Data> data;

这是 kotlin 语言中类似的主题link

【讨论】:

  • 根据@Michał Ziober 的回答,我做了一些更正以完成这项工作@Override public T deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException { JsonElement jsonElement; if (json.isJsonArray()) { jsonElement = json.getAsJsonArray().get(0); return context.deserialize(jsonElement,type); } else { jsonElement = json.getAsJsonObject(); return context.deserialize(jsonElement, type); //Data.class works aswell } } 谢谢您的回答。
猜你喜欢
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多