mik*_*lar 104 java android json gson retrofit
我正在从我的Android应用程序中使用API,并且所有JSON响应都是这样的:
{
'status': 'OK',
'reason': 'Everything was fine',
'content': {
< some data here >
}
Run Code Online (Sandbox Code Playgroud)
问题是,我所有的POJO有status
,reason
字段,里面content
领域是真正的POJO我想要的.
有没有办法创建一个Gson的自定义转换器来提取总是content
字段,所以改造返回适当的POJO?
Bri*_*ach 160
您将编写一个返回嵌入对象的自定义反序列化器.
假设您的JSON是:
{
"status":"OK",
"reason":"some reason",
"content" :
{
"foo": 123,
"bar": "some value"
}
}
Run Code Online (Sandbox Code Playgroud)
然后你有一个Content
POJO:
class Content
{
public int foo;
public String bar;
}
Run Code Online (Sandbox Code Playgroud)
然后你写一个反序列化器:
class MyDeserializer implements JsonDeserializer<Content>
{
@Override
public Content deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, Content.class);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,如果您构造一个Gson
with GsonBuilder
并注册反序列化器:
Gson gson =
new GsonBuilder()
.registerTypeAdapter(Content.class, new MyDeserializer())
.create();
Run Code Online (Sandbox Code Playgroud)
您可以直接将您的JSON反序列化为Content
:
Content c = gson.fromJson(myJson, Content.class);
Run Code Online (Sandbox Code Playgroud)
编辑以添加评论:
如果您有不同类型的消息但它们都具有"内容"字段,则可以通过执行以下操作使反序列化器具有通用性:
class MyDeserializer<T> implements JsonDeserializer<T>
{
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException
{
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
return new Gson().fromJson(content, type);
}
}
Run Code Online (Sandbox Code Playgroud)
您只需为每种类型注册一个实例:
Gson gson =
new GsonBuilder()
.registerTypeAdapter(Content.class, new MyDeserializer<Content>())
.registerTypeAdapter(DiffContent.class, new MyDeserializer<DiffContent>())
.create();
Run Code Online (Sandbox Code Playgroud)
当你调用.fromJson()
类型被带入反序列化器时,它应该适用于所有类型.
最后在创建Retrofit实例时:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Run Code Online (Sandbox Code Playgroud)
小智 14
@ BrianRoach的解决方案是正确的解决方案.值得注意的是,在您需要自定义的嵌套自定义对象的特殊情况下TypeAdapter
,您必须TypeAdapter
使用新的GSON实例注册,否则TypeAdapter
永远不会调用第二个.这是因为我们Gson
在自定义反序列化器中创建了一个新实例.
例如,如果您有以下json:
{
"status": "OK",
"reason": "some reason",
"content": {
"foo": 123,
"bar": "some value",
"subcontent": {
"useless": "field",
"data": {
"baz": "values"
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
并且您希望将此JSON映射到以下对象:
class MainContent
{
public int foo;
public String bar;
public SubContent subcontent;
}
class SubContent
{
public String baz;
}
Run Code Online (Sandbox Code Playgroud)
你需要注册SubContent
的TypeAdapter
.为了更强大,您可以执行以下操作:
public class MyDeserializer<T> implements JsonDeserializer<T> {
private final Class mNestedClazz;
private final Object mNestedDeserializer;
public MyDeserializer(Class nestedClazz, Object nestedDeserializer) {
mNestedClazz = nestedClazz;
mNestedDeserializer = nestedDeserializer;
}
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
// Get the "content" element from the parsed JSON
JsonElement content = je.getAsJsonObject().get("content");
// Deserialize it. You use a new instance of Gson to avoid infinite recursion
// to this deserializer
GsonBuilder builder = new GsonBuilder();
if (mNestedClazz != null && mNestedDeserializer != null) {
builder.registerTypeAdapter(mNestedClazz, mNestedDeserializer);
}
return builder.create().fromJson(content, type);
}
}
Run Code Online (Sandbox Code Playgroud)
然后像这样创建它:
MyDeserializer<Content> myDeserializer = new MyDeserializer<Content>(SubContent.class,
new SubContentDeserializer());
Gson gson = new GsonBuilder().registerTypeAdapter(Content.class, myDeserializer).create();
Run Code Online (Sandbox Code Playgroud)
这可以很容易地用于嵌套的"内容"情况,只需传入一个MyDeserializer
带有null值的新实例.
Mat*_*lak 10
有点晚,但希望这会对某人有所帮助.
只需创建以下TypeAdapterFactory即可.
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("content")) {
jsonElement = jsonObject.get("content");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
Run Code Online (Sandbox Code Playgroud)
并将其添加到您的GSON构建器中:
.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
Run Code Online (Sandbox Code Playgroud)
要么
yourGsonBuilder.registerTypeAdapterFactory(new ItemTypeAdapterFactory());
Run Code Online (Sandbox Code Playgroud)
小智 6
继续Brian的想法,因为我们几乎总是拥有许多REST资源,每个资源都有自己的根,所以推广反序列化可能很有用:
class RestDeserializer<T> implements JsonDeserializer<T> {
private Class<T> mClass;
private String mKey;
public RestDeserializer(Class<T> targetClass, String key) {
mClass = targetClass;
mKey = key;
}
@Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
JsonElement content = je.getAsJsonObject().get(mKey);
return new Gson().fromJson(content, mClass);
}
}
Run Code Online (Sandbox Code Playgroud)
然后从上面解析样本有效负载,我们可以注册GSON反序列化器:
Gson gson = new GsonBuilder()
.registerTypeAdapter(Content.class, new RestDeserializer<>(Content.class, "content"))
.build();
Run Code Online (Sandbox Code Playgroud)
几天前有同样的问题.我用响应包装器类和RxJava转换器来解决这个问题,我认为这是一个非常灵活的解决方案:
包装:
public class ApiResponse<T> {
public String status;
public String reason;
public T content;
}
Run Code Online (Sandbox Code Playgroud)
当状态不正常时,抛出自定义异常:
public class ApiException extends RuntimeException {
private final String reason;
public ApiException(String reason) {
this.reason = reason;
}
public String getReason() {
return apiError;
}
}
Run Code Online (Sandbox Code Playgroud)
Rx变压器:
protected <T> Observable.Transformer<ApiResponse<T>, T> applySchedulersAndExtractData() {
return observable -> observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(tApiResponse -> {
if (!tApiResponse.status.equals("OK"))
throw new ApiException(tApiResponse.reason);
else
return tApiResponse.content;
});
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
// Call definition:
@GET("/api/getMyPojo")
Observable<ApiResponse<MyPojo>> getConfig();
// Call invoke:
webservice.getMyPojo()
.compose(applySchedulersAndExtractData())
.subscribe(this::handleSuccess, this::handleError);
private void handleSuccess(MyPojo mypojo) {
// handle success
}
private void handleError(Throwable t) {
getView().showSnackbar( ((ApiException) throwable).getReason() );
}
Run Code Online (Sandbox Code Playgroud)
我的主题: Retrofit 2 RxJava - Gson - "全局"反序列化,更改响应类型
归档时间: |
|
查看次数: |
89318 次 |
最近记录: |