自定义转换器与Moshi的子类

我有一个用户类。 还有两个子类。 父母和孩子。 我使用{“user”:“…”}从服务器获取json,并且需要根据user.type将其转换为父级或子级

据我所知,我需要添加自定义转换器这种方式:

Moshi moshi = new Moshi.Builder() .add(new UserAdapter()) .build(); 

这是我的UserAdapter的实现。 我知道这是虚构的,但即使这样:

 public class UserAdapter { @FromJson User fromJson(String userJson) { Moshi moshi = new Moshi.Builder().build(); try { JSONObject jsonObject = new JSONObject(userJson); String accountType = jsonObject.getString("type"); switch (accountType) { case "Child": JsonAdapter<Child> childJsonAdapter = moshi.adapter(Child.class); return childJsonAdapter.fromJson(userJson); case "Parent": JsonAdapter<Parent> parentJsonAdapter = moshi.adapter(Parent.class); return parentJsonAdapter.fromJson(userJson); } } catch (JSONException | IOException e) { e.printStackTrace(); } return null; } @ToJson String toJson(User user) { Moshi moshi = new Moshi.Builder().build(); JsonAdapter<User> jsonAdapter = moshi.adapter(User.class); String toJson = jsonAdapter.toJson(user); return toJson; } 

首先我得到以下例外的代码。

 com.squareup.moshi.JsonDataException: Expected a string but was BEGIN_OBJECT at path $.user 

其次,我相信有一个更好的方法来做到这一点。 请指教。

UPD。 这里是stacktrace的错误:

  com.squareup.moshi.JsonDataException: Expected a name but was BEGIN_OBJECT at path $.user at com.squareup.moshi.JsonReader.nextName(JsonReader.java:782) at com.squareup.moshi.ClassJsonAdapter.fromJson(ClassJsonAdapter.java:141) at com.squareup.moshi.JsonAdapter$1.fromJson(JsonAdapter.java:68) at com.squareup.moshi.JsonAdapter.fromJson(JsonAdapter.java:33) at retrofit.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:33) at retrofit.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:23) at retrofit.OkHttpCall.parseResponse(OkHttpCall.java:148) at retrofit.OkHttpCall.execute(OkHttpCall.java:116) at retrofit.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:111) at retrofit.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:88) at rx.Observable$2.call(Observable.java:162) at rx.Observable$2.call(Observable.java:154) at rx.Observable$2.call(Observable.java:162) at rx.Observable$2.call(Observable.java:154) at rx.Observable.unsafeSubscribe(Observable.java:7710) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) at java.lang.Thread.run(Thread.java:818) 

Solutions Collecting From Web of "自定义转换器与Moshi的子类"

这在我看来就像你想要按照自定义de /序列化你的JSON数据的例子: https : //github.com/square/moshi#another-example

它使用一个对应于JSON结构的中间类,Moshi会自动为它膨胀。 然后,你可以使用膨胀的数据来build立你的专门的用户类。 例如:

 // Intermediate class with JSON structure class UserJson { // Common JSON fields public String type; public String name; // Parent JSON fields public String occupation; public Long salary; // Child JSON fields public String favorite_toy; public Integer grade; } abstract class User { public String type; public String name; } final class Parent extends User { public String occupation; public Long salary; } final class Child extends User { public String favoriteToy; public Integer grade; } 

现在,适配器:

 class UserAdapter { // Note that you pass in a `UserJson` object here @FromJson User fromJson(UserJson userJson) { switch (userJson.type) { case "Parent": final Parent parent = new Parent(); parent.type = userJson.type; parent.name = userJson.name; parent.occupation = userJson.occupation; parent.salary = userJson.salary; return parent; case "Child": final Child child = new Child(); child.type = userJson.type; child.name = userJson.name; child.favoriteToy = userJson.favorite_toy; child.grade = userJson.grade; return child; default: return null; } } // Note that you return a `UserJson` object here. @ToJson UserJson toJson(User user) { final UserJson json = new UserJson(); if (user instanceof Parent) { json.type = "Parent"; json.occupation = ((Parent) user).occupation; json.salary = ((Parent) user).salary; } else { json.type = "Child"; json.favorite_toy = ((Child) user).favoriteToy; json.grade = ((Child) user).grade; } json.name = user.name; return json; } } 

我认为这更清洁,并允许Moshi做它的事情,这是从JSON创build对象和从对象创buildJSON。 没有与老式的JSONObject

去testing:

 Child child = new Child(); child.type = "Child"; child.name = "Foo"; child.favoriteToy = "java"; child.grade = 2; Moshi moshi = new Moshi.Builder().add(new UserAdapter()).build(); try { // Serialize JsonAdapter<User> adapter = moshi.adapter(User.class); String json = adapter.toJson(child); System.out.println(json); // Output is: {"favorite_toy":"java","grade":2,"name":"Foo","type":"Child"} // Deserialize // Note the cast to `Child`, since this adapter returns `User` otherwise. Child child2 = (Child) adapter.fromJson(json); System.out.println(child2.name); // Output is: Foo } catch (IOException e) { e.printStackTrace(); } 

您可能试图按照以下方法实现您的parsing: https : //github.com/square/moshi#custom-type-adapters

有String用作@FromJson方法的参数,所以它可以神奇地分析到一些映射助手类或string,我们必须手动parsing它,对吗? 其实不,你可以使用映射助手类映射。

因此,你的exceptionExpected a string but was BEGIN_OBJECT at path $.user是由Moshi试图获得该用户作为一个string(因为这就是你的适配器暗示),而它只是另一个对象。

我不喜欢将所有可能的字段parsing为一些助手类,因为在多态性的情况下,类可能会变得非常大,你需要依赖或记住/注释代码。

你可以把它作为一个地图来处理 – 这是未知types的默认模型 – 并将其转换为json,所以你的情况看起来像这样:

  @FromJson User fromJson(Map<String, String> map) { Moshi moshi = new Moshi.Builder().build(); String userJson = moshi.adapter(Map.class).toJson(map); try { JSONObject jsonObject = new JSONObject(userJson); String accountType = jsonObject.getString("type"); switch (accountType) { case "Child": JsonAdapter<Child> childJsonAdapter = moshi.adapter(Child.class); return childJsonAdapter.fromJson(userJson); case "Parent": JsonAdapter<Parent> parentJsonAdapter = moshi.adapter(Parent.class); return parentJsonAdapter.fromJson(userJson); } } catch (JSONException | IOException e) { e.printStackTrace(); } return null; } 

当然,你可以直接处理地图:检索“types”string,然后parsing地图的其余部分select类。 那么根本就不需要使用JSONObject,而不必依赖于Android,并且更容易testingparsing。

  @FromJson User fromJson(Map<String, String> map) { Moshi moshi = new Moshi.Builder().build(); try { String userJson = moshi.adapter(Map.class).toJson(map); switch (map.get("type")) { case "Child": JsonAdapter<Child> childJsonAdapter = moshi.adapter(Child.class); return childJsonAdapter.fromJson(userJson); case "Parent": JsonAdapter<Parent> parentJsonAdapter = moshi.adapter(Parent.class); return parentJsonAdapter.fromJson(userJson); } } catch (IOException e) { e.printStackTrace(); } return null; }