使用@JsonIdentityInfo 时杰克逊“MismatchedInputException:找不到实例的对象 ID”

Posted

技术标签:

【中文标题】使用@JsonIdentityInfo 时杰克逊“MismatchedInputException:找不到实例的对象 ID”【英文标题】:Jackson "MismatchedInputException: No Object Id found for an instance" when using @JsonIdentityInfo 【发布时间】: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,它几乎解决了我的所有问题。 【参考方案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(
    ...
)

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

https://www.baeldung.com/jackson-deserialization https://www.baeldung.com/jackson-custom-serialization

【讨论】:

感谢您的回答,非常感谢。 UserUserInfo 是双向的,所以当我请求 UserInfo 时,我也期望 User 在响应中,反之亦然。所以 JsonManagedReference 并没有解决我的问题。 对不起,我相信我没有写出完整的答案,所以我编辑了它,并包含了另一个最强大的替代方法,即编写自定义(反)序列化程序 不是最好的解决方案,但它对我有用。谢谢!

以上是关于使用@JsonIdentityInfo 时杰克逊“MismatchedInputException:找不到实例的对象 ID”的主要内容,如果未能解决你的问题,请参考以下文章

JSON 序列化与关系和@JsonIdentityInfo

尝试使用杰克逊序列化空瞬间时出错

JSON 使用杰克逊库相同对象的字段,但在必要时打印不同的值

杰克逊在序列化时忽略@Size

杰克逊:当调用不同的 Rest EndPoint 时,同一实体上的多个序列化器

当我尝试反序列化对象列表时,杰克逊抛出错误映射异常