【问题标题】:Jackson "MismatchedInputException: No Object Id found for an instance" when using @JsonIdentityInfo使用@JsonIdentityInfo 时杰克逊“MismatchedInputException:找不到实例的对象 ID”
【发布时间】:2020-07-20 19:04:48
【问题描述】:

首先,当我得到User 实体时,我试图阻止 JSON 中的递归。为此,我在User 类中添加了@JsonIdentityInfo 注释。它在序列化时按预期工作(当我得到 User 时),但在反序列化时(在注册 User 时)Jackson 会返回:

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: No Object Id found for an instance of `.entity.User`, to assign to property 'id'; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: No Object Id found for an instance of `.entity.User`, to assign to property 'id'
 at [Source: (PushbackInputStream); line: 13, column: 1]]

用户.kt

@Entity
@Table(name = "user")
@Audited
@EntityListeners(AuditingEntityListener::class)
@Validated
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id", scope = Int::class)
data class User(

        @field:Id
        @field:GeneratedValue(strategy = GenerationType.IDENTITY)
        var id: Int,

        @Valid
        @Size(
                min = 4,
                message = "Username '\${validatedValue}' should be at least {min} characters long"
        )
        @NotEmpty(message = "asd")
        var username: String = "",

        ...

        @field:OneToOne(cascade = [CascadeType.ALL], optional = true)
        var userInfo: UserInfo? = null

)

我正在发送用于反序列化的 JSON(在删除 @JsonIdentityInfo 时有效):

{
    "username": "qwert",
    "password": "tyui",
    "email": "asdasd",
    "phone": ""
}

注册方法:

    @PostMapping("/users/signup")
    fun addUser(@RequestBody @Valid user: User): JSendResponse<Unit> {

        user.password = passwordEncoder.encode(user.password)
        userService.save(user)

        return JSendResponse(data = Unit)
    }

统一更新: 这个问题的解决方案是在 Json 中显式设置 id 字段,因为它是在持久性级别上生成的。但我不想显式设置它,因为前端不知道新对象的id

【问题讨论】:

  • 您使用的是什么版本的 jackson-databind?
  • 它是 2.9.9.3 和 jackson-core 2.9.9。实际上,我找到了解决方案,但我对此并不满意。更新了问题。
  • 这似乎是某些版本的jackson-databind github.com/FasterXML/jackson-databind/issues/1367中的错误
  • 现在我在存储库层之上使用 GraphQL,它几乎解决了我的所有问题。

标签: json spring-boot kotlin jackson deserialization


【解决方案1】:

JsonIdentityInfo 用于避免循环引用,这会导致堆栈溢出。 Jackson 应该在找到此类引用时分配一个“id”。

似乎某些版本的 jackson 在此功能上存在错误:github.com/FasterXML/jackson-databind/issues/1367 因此,您应该尝试更改 jackson 的版本,或者执行以下替代方案:

一些替代方案是:

在相互引用的字段上使用注解JsonManagedReference和JsonBackReference;在您的情况下,“用户”和“用户信息”。 JsonManagedReference 是正常(反)序列化的一面。 JsonBackReference 是引用的后面部分,不会被(反)序列化。 确保在反序列化后设置“反向引用”部分。

例如,如果您的 UserInfo 属性是托管引用,则在 User 类中,您应该有以下结果:

class User {
    Long id;
    @JsonManagedReference
    UserInfo userInfo;
}

class UserInfo {
    String nickname;
    String moto;
    @JsonBackReference
    User user;
    ...
}

// OUTPUT EXAMPLE
public static void main(String[] args) throws JsonProcessingException {
    UserInfo info = new UserInfo("johndoe","it works", null);
    User user = new User(1L, info);
    info.setUser(user);
    ObjectMapper om = new ObjectMapper();
    System.out.println("user = " + om.writeValueAsString(user));
    System.out.println("userinfo = " + om.writeValueAsString(info));
}

输出

user = {
   "id":1,
   "userInfo":{
     "nickname":"johndoe",
     "moto":"it works"
    }
}
userinfo = {
   "nickname":"johndoe",
   "moto":"it works"
}

另一种选择是为您的实体创建自定义序列化器和自定义反序列化器。这是最强大的(反)序列化方式;您可以对双方进行(反)序列化,甚至添加自定义属性。

@JsonDeserialize(using = UserDeserializer.class)
@JsonSerialize(using = UserSerializer.class)
data class User(
    ...
)

要创建自定义(反)序列化程序,您可以按照以下教程进行操作:

【讨论】:

  • 感谢您的回答,非常感谢。 UserUserInfo 是双向的,所以当我请求 UserInfo 时,我也期望 User 在响应中,反之亦然。所以 JsonManagedReference 并没有解决我的问题。
  • 对不起,我相信我没有写出完整的答案,所以我编辑了它,并包含了另一个最强大的替代方法,即编写自定义(反)序列化程序
  • 不是最好的解决方案,但它对我有用。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-26
  • 2020-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
相关资源
最近更新 更多