使用@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【讨论】:
感谢您的回答,非常感谢。User
和 UserInfo
是双向的,所以当我请求 UserInfo
时,我也期望 User
在响应中,反之亦然。所以 JsonManagedReference 并没有解决我的问题。
对不起,我相信我没有写出完整的答案,所以我编辑了它,并包含了另一个最强大的替代方法,即编写自定义(反)序列化程序
不是最好的解决方案,但它对我有用。谢谢!以上是关于使用@JsonIdentityInfo 时杰克逊“MismatchedInputException:找不到实例的对象 ID”的主要内容,如果未能解决你的问题,请参考以下文章
JSON 使用杰克逊库相同对象的字段,但在必要时打印不同的值