如何使用 Moshi、Retrofit 和 Java 处理包装数据?

huangapple go评论83阅读模式
英文:

how can I work with wrapped data using moshi, retrofit and java?

问题

我正在使用一个API,其中所有数据都包装在一个自定义对象中(如下所示),因此我不能使用Moshi直接将retrofit body转换为我的模型。在这种情况下,最佳的使用Moshi的方法是什么?

集合终点

{
"status": 200,
"data": [
{
"id": 28122,
"name": "Abandonei",
"counts": {
"books": 3
}
},
{
"id": 21091,
"name": "Lendo",
"counts": {
"books": 6
}
},
],
"errors": [],
"pagination": {
"after": 2,
"hasNextPage": true
}
}

相同的JSON结构在所有API端点中都被使用,默认字段包括:

{
"status": 200,
"data": [],
"errors": [],
"pagination": {
"after": 1,
"hasNextPage": true
}
}

我的Collection模型:

public class BookCollection {
public long id;
public String name;
public ArrayList books;

public BookCollection(long id, String name) {
    this.id = id;
    this.name = name;
}

}

英文:

I'm working with an API where all data are wrapped in a custom object (see below), so I cannot use moshi to convert the retrofit body direct to my models. What is the best way to work with moshi in this case?

#COLLECTIONS ENDPOINT

{
    "status": 200,
    "data": [
        {
            "id": 28122,
            "name": "Abandonei",
            "counts": {
                "books": 3
            }
        },
        {
            "id": 21091,
            "name": "Lendo",
            "counts": {
                "books": 6
            }
        },
    ],
    "errors": [],
    "pagination": {
        "after": 2,
        "hasNextPage": true
    }
}

The same json structure is used in all api endpoints, the default fields are:

{
    "status": 200,
    "data": [],
    "errors": [],
    "pagination": {
        "after": 1,
        "hasNextPage": true
    }
}

My Collection model:

public class BookCollection {
    public long id;
    public String name;
    public ArrayList<Book> books;

    public BookCollection(long id, String name) {
        this.id = id;
        this.name = name;
    }
}

答案1

得分: 0

为了避免为每个模型创建父类,我已经实现了一种使用接收泛型类型的类的方式。

为了使这个工作起来,我已经将Moshi类更改为Gson。

我的模型:

public class BookCollection {
    public long id;
    public String name;
    public ArrayList<Book> books;

    public BookCollection(long id, String name) {
        this.id = id;
        this.name = name;
    }
}

用于解包JSON数据的包装类:

public class ApiWrapper<T> {
    public final int status;
    public final T data;
    public final List<ApiError> errors = new ArrayList<>();

    public ApiWrapper(int status, T data, List<ApiError> errors) {
        this.status = status;
        this.data = data;
        this.errors.addAll(errors);
    }
}

上面的类引用的错误类:

public class ApiError {
    public int code;
    public String message;
    public String error;
}

用法:

public interface NetAPI {
    @GET("me/collections")
    Call<ResponseBody> getCollections(@Header("Authorization") String auth);
}

public class CollectionViewModel extends ViewModel {
    private final MutableLiveData<List<Collection>> collections = new MutableLiveData<>();
    private final MutableLiveData<Boolean> loading = new MutableLiveData<>();
    private final MutableLiveData<Boolean> collectionError = new MutableLiveData<>();

    private Call<ResponseBody> call;

    private void fetchCollections() {
        loading.setValue(true);
        call = Api.getInstance().getCollections(TOKEN);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                try {
                    collectionError.setValue(false);

                    //这是关键部分
                    Gson gson = new Gson();
                    ApiWrapper<List<Collection>> apiResponse = null;
                    apiResponse = gson.fromJson(response.body().string(), new TypeToken<ApiWrapper<List<Collection>>>(){}.getType());

                    collections.setValue(apiResponse.data);

                    loading.setValue(false);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.e(getClass().getSimpleName(), "Error loading data", t);
                collectionError.setValue(true);
                loading.setValue(false);
            }
        });
    }
}

通过这种方式,我可以重用我的ApiWrapper类,用于任何模型(Books,Users,Login等)。

谢谢。

英文:

To avoid create a parent class to each model, I have implemented a way to use a class that receive a generic type.

To put this to work I've changed the Moshi class to Gson.

My model:

public class BookCollection {
    public long id;
    public String name;
    public ArrayList&lt;Book&gt; books;

    public BookCollection(long id, String name) {
        this.id = id;
        this.name = name;
    }
}

The wrapper class used to unwrap the json data:

public class ApiWrapper&lt;T&gt; {
public final int status;
public final T data;
public final List&lt;ApiError&gt; errors = new ArrayList&lt;&gt;();
public ApiWrapper(int status, T data, List&lt;ApiError&gt; errors) {
this.status = status;
this.data = data;
this.errors.addAll(errors);
}
}

The Errors class, referenced in the class above:

public class ApiError {
public int code;
public String message;
public String error;
}

Usage:

public interface NetAPI {
@GET(&quot;me/collections&quot;)
Call&lt;ResponseBody&gt; getCollections(@Header(&quot;Authorization&quot;) String auth);
}
public class CollectionViewModel extends ViewModel {
private final MutableLiveData&lt;List&lt;Collection&gt;&gt; collections = new MutableLiveData&lt;&gt;();
private final MutableLiveData&lt;Boolean&gt; loading = new MutableLiveData&lt;&gt;();
private final MutableLiveData&lt;Boolean&gt; collectionError = new MutableLiveData&lt;&gt;();
private Call&lt;ResponseBody&gt; call;
private void fetchCollections() {
loading.setValue(true);
call = Api.getInstance().getCollections(TOKEN);
call.enqueue(new Callback&lt;ResponseBody&gt;() {
@Override
public void onResponse(Call&lt;ResponseBody&gt; call, Response&lt;ResponseBody&gt; response) {
try {
collectionError.setValue(false);
//THE SECRET
Gson gson = new Gson();
ApiWrapper&lt;List&lt;Collection&gt;&gt; apiResponse = null;
apiResponse = gson.fromJson(response.body().string(), new TypeToken&lt;ApiWrapper&lt;List&lt;Collection&gt;&gt;&gt;(){}.getType());
collections.setValue(apiResponse.data);
loading.setValue(false);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Call&lt;ResponseBody&gt; call, Throwable t) {
Log.e(getClass().getSimpleName(), &quot;Error loading data&quot;, t);
collectionError.setValue(true);
loading.setValue(false);
}
});
}
}

At this way I can reuse my ApiWrapper class to any model (Books, Users, Login, etc).

Thanks.

答案2

得分: -1

你需要设置gson/moshi来使用你为json到对象映射创建的类。以下是这些Java类的示例。你也可以在Kotlin中使用数据类。对于moshi,你将需要创建适配器来帮助进行json到对象的映射。

public class CollectionResponse {
    public int status;
    public List<BookCollection> data;
    public List<Error> errors;
    public Pagination pagination;
}

public class Pagination {
    public int after;
    public boolean hasNextPage;
}

public class BookCollection {
    public long id;
    public String name;
    public Count counts;
}

public class Count {
    public int books;
}

public class Error {
    
}
英文:

You will need to setup gson/moshi to use your classes that you have created for the json to object mapping. Here is an example of what those java classes would look like. You can use data classes in kotlin as well. For moshi, you will have to create the adapter to help with the json to object mapping.

publci class CollectionResponse {
public int status;
public List&lt;BookCollection&gt; data;
public List&lt;Error&gt; errors;
public Pagination pagination;
}
public class Pagination {
public int after;
public boolean hasNextPage;
}
public class BookCollection {
public long id;
public String name;
public Count counts;
}
public Count {
public int books;
}
public class Error {
}

huangapple
  • 本文由 发表于 2020年7月30日 12:27:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63166188.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定