Retrofit 2.0、Jackson 和多态性:找不到名称为 <...> 的创建者 属性

Retrofit 2.0, Jackson and polymorphism: Could not find creator property with name <...>

我目前正在使用 RetroFit 和 Jackson 的 REST API。当基于搜索查询检索用户时,请考虑以下响应 JSON:

找到一个结果

{
    name: "API name",
    results: {
        count: 1
        users: {
            username: "username",
            age: 15
        }
    }
}

找到多个结果

{
    name: "API name",
    results: {
        count: 2,
        users: [{
            username: "username1",
            age: 18
        }, {
            username: "username2",
            age: 19
        }]
    }
}

如您所见,users-属性包含动态JSON:根据查找结果,"users"的值可以是用户对象的列表,或者1 个用户对象。

因此,我使用多态性设计了我的 Java POJO,如下所示:

public class UserResponse {
    @JsonProperty("name")
    private String apiName

    @JsonProperty("results")
    private AResultList resultList

    //Getters & Setters

    //Constructor
    @JsonCreator
    public UserResponse(@JsonProperty("name") String name, @JsonProperty("results") AResultList r) {
        this.apiName = name;
        this.resultList = r;
    }
}

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "classType"
        )
@JsonSubTypes({
    @JsonSubTypes.Type(value = ResultObject.class, name="ResultObject"),
    @JsonSubTypes.Type(value = ResultList.class, name="ResultList")
})
public abstract class AResultList {
    @JsonProperty("count)
    private long totalCount;

    //Getters & Setters

    //Constructors
    @JsonCreator
    public AResultList(@JsonProperty("count) long count) {
        this.totalCount = count;
    }
}

public class ResultObject extends AResultList {
    @JsonProperty("users")
    private User user;

    //Getters & Setters

    //Constructor
    @JsonCreator
    public ResultObject(@JsonProperty("count) long count, @JsonProperty("users") User u) {
        super(count);

        this.user = u;
    }
}

public class ResultList extends AResultList {
    @JsonProperty("users")
    private List<User> users;

    //Getters & Setters

    //Constructor
    @JsonCreator
    public ResultObject(@JsonProperty("count) long count, @JsonProperty("users") List<User> u) {
        super(count);

        this.users = u;
    }
}

public class User {
    @JsonProperty("username")
    private String username;

    @JsonProperty("age")
    private long userAge;

    //Getters & Setters

    //Constructor
    @JsonCreator
    public User(@JsonProperty("username") String u, @JsonProperty("age") long a) {
        this.userAge = a;
        this.username = u;
    }
}

供您参考:用于实例化 RetroFit 的片段

ObjectMapper o = new ObjectMapper();

retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(JacksonConverterFactory.create(o))
                .client(okClient)
                .build();

但是,尝试检索此信息会导致以下错误:

com.fasterxml.jackson.databind.JsonMappingException: Could not find creator property with name 'count' (in class org.namespace.AResultList)
at [Source: java.io.InputStreamReader@41cd2648; line: 1, column: 1]

我已经在这个 "dynamic data" 问题上卡住了 2 天了。已经尝试使用 GSON 库和许多其他东西但无济于事。所以想问一下:
- 为什么杰克逊会造成这种情况?这是一个错误吗?一些用户已经问过这个问题,但提供的解决方案不适用于我的情况。
- 这是处理动态数据的正确方法吗?

我已经在不使用多态 类(并且只从 API 中检索 1 个用户)和对象映射的情况下测试了我的代码。问题是由多态性引起的,但我不知道如何解决它。

您在 Jackson 注释中指定的多态配置表明 {"classType":"ResultObject",...}{"classType":"ResultList",...} 将出现在 JSON-- 但事实并非如此。我不确定您收到的错误的确切原因,但它似乎是在摘要 class 上寻找创建者,因为没有类型 属性.

[对于多态性,Jackson 需要从 JSON 中读取一些内容来确定此时要反序列化的 bean 类型:您实际上没有,只有 array/objectness users。因此我认为 Jackson 的多态性支持不适合这种情况]

事实上,要允许 属性 获取单个项目或项目列表,您只需启用 ACCEPT_SINGLE_VALUE_AS_ARRAY 反序列化功能。但是,这需要全局启用,似乎没有办法将其定位到特定的 属性.

public static final class UserResponse {
    public String name;
    public Results results;

    public static final class Results {
        public int count;
        public List<User> users;
    }

    public static final class User {
        public String username;
        public int age;
    }
}

@Test
public void reads_single_result() throws Exception {
    ObjectReader reader = new ObjectMapper().reader(UserResponse.class)
            .with(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
            .with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES).with(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
    UserResponse response = reader.readValue("{ name: 'API name', results: { count: 1,"
            + " users: { username: 'username', age: 15 } } }");
    assertThat(response.results.users, iterableWithSize(1));
    assertThat(response.results.users.get(0).username, equalTo("username"));
}

@Test
public void reads_two_results() throws Exception {
    ObjectReader reader = new ObjectMapper().reader(UserResponse.class)
            .with(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
            .with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES).with(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
    UserResponse response = reader.readValue("{ name: 'API name', results: { count: 2,"
            + " users: [{ username: 'username1', age: 18 }, { username: 'username2', age: 19 }] } }");
    assertThat(response.results.users, iterableWithSize(2));
    assertThat(response.results.users.get(0).username, equalTo("username1"));
    assertThat(response.results.users.get(1).username, equalTo("username2"));
}

哦,如果你想摆脱其中无用的结果对象,你可以使用转换器来实现:

public static final class UserResponseWithConverter {
    public String name;
    @JsonProperty("results")
    @JsonDeserialize(converter = ConvertResultsToUserList.class)
    public List<User> users;

    public static final class Results {
        public int count;
        public List<User> users;
    }

    public static final class User {
        public String username;
        public int age;
    }

    public static final class ConvertResultsToUserList extends StdConverter<Results, List<User>> {
        @Override
        public List<User> convert(Results value) {
            return value.users;
        }
    }
}

配置正确的序列化留作 reader 的练习;)