改造 GSON 响应抽象映射

Retrofit GSON response abstract mapping

我通过改造发出了一个简单的 HTTP GET 请求,并尝试将 json 响应映射到我的模型。问题是,json return 一个数组的倍数 Shapes,Shape 是一个抽象的 class,所以它可以是 Square,Circle 等。每个 shape 都有自己指定的模型,所以不同的领域。如何将这个 Shape 数组映射到模型?

Web 服务 json 响应

{
  "requestId": 0,
  "totalShapes": 2,
  "shapes": [
    {
      "circle": {
        "code": 1,
        "radius": 220
        "color" : "blue"
      }
    },
    {
      "square": {
        "code": 1,
        "size": 220
      }
    }
  ]
}

主要结果映射:

public class Result {
  @SerializedName("requestId") private int requestId;
  @SerializedName("totalShapes") private int totalShapes;
  @SerializedName("shapes") private List<Shape> shapes;
}

摘要class:

public abstract class Shape implements Serializable {
}

圆圈:

public class Circle {
  @SerializedName("code") private int code;
  @SerializedName("radius") private int radius;
  @SerializedName("color") private String color;
  // + getters...
}

正方形:

public class Square {
  @SerializedName("code") private int code;
  @SerializedName("size") private int size;
  // + getters...
}

您可以实现一个像工厂一样的自定义形状反序列化器。基于形状对象的键,您可以将其反序列化为相应的类型。

class ShapeDeserializer implements JsonDeserializer<Shape> {
    @Override
    public Shape deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Map.Entry<String, JsonElement> entry = json.getAsJsonObject().entrySet().iterator().next();
        switch(entry.getKey()) {
            case "circle":
                return context.deserialize(entry.getValue(), Circle.class);
            case "square":
                return context.deserialize(entry.getValue(), Square.class);
            default:
                throw new IllegalArgumentException("Can't deserialize " + entry.getKey());
        }
    }
}

然后在解析器中注册它

Gson gson = 
    new GsonBuilder().registerTypeAdapter(Shape.class, new ShapeDeserializer())
                     .create();

你使用它:

Result result = gson.fromJson(myJson, Result.class);
//Result{requestId=0, totalShapes=2, shapes=[Circle{code=1, radius=220, color='blue'}, Square{code=2, size=220}]}

如果密钥与 class 名称完全匹配,您可以直接使用 Class.forName(您需要首先将密钥大写)。

另请注意:

  • 你可以将 code 属性 移动到摘要 Shape class 中,如果它出现在每个子classes
  • 我假设您有一个且只有一个形状与键关联。如果您可以在与键 "circle" 关联的同一个 JsonObject 中有两个圆圈,您将需要更复杂的逻辑来解析它。如果不是这种情况(尽管 RFC 不建议使用具有相同键的不同键值对),它应该可以正常工作。