自定义 Jackson 解串器无法确定目标类别
我目前有一个 Join 对象,我正在尝试为其编写自定义反序列化器。我们将数据库键和对象的 Java 类名存储在 JSON 中,看起来像这样:
{
"@class": "com.mysite.data.ExpertFor",
"id": "expert-for-1",
"leftId": "user-profile-1",
"leftType": "com.mysite.data.UserProfile",
"rightId": "hotel-1",
"rightType": "com.mysite.data.Hotel"
}
我们这样做是为了不必将实际对象存储在数据库中;相反,我们在反序列化它时检索它。问题是我的自定义序列化程序无法看到 JSON 流中的 @class
元素 - 因此,在读取所有数据后,它无法正确实例化所需的对象。这是我的反序列化器:
public Join deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Map<String, Object> values = new HashMap<String, Object>();
// Loop through the JSON document and pull out all
// the values and put them into the map
JsonToken token = jp.nextToken();
while (token != JsonToken.END_OBJECT) {
logger.info("Non-data field: [" + token + "], name is [" + jp.getCurrentName() + "], text is [" + jp.getText() + "]");
if (token == JsonToken.VALUE_STRING) {
values.put(jp.getCurrentName(), jp.getText());
}
token = jp.nextToken();
}
// remainder of code omitted for brevity
}
现在,写入日志的 Log4J 行显示了我期望的 JSON 的所有适当元素,除了
Data field: [VALUE_STRING], name is [id], text is [expert-for-1]
Data field: [VALUE_STRING], name is [leftId], text is [user-profile-1]
Data field: [VALUE_STRING], name is [rightId], text is [hotel-1]
Data field: [VALUE_STRING], name is [leftType], text is [com.mysite.data.UserProfile]
Data field: [VALUE_STRING], name is [rightType], text is [com.mysite.data.Hotel]
我尝试添加 @ 的
注释到我的类,就像,@class
元素JsonTypeInfo
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
但似乎该元素被代码“吞没”,我不知道如何获取它。我需要这个,因为 Join
的任何后代都需要反序列化。以下是有关该类所在层次结构的一些附加信息:
添加 @TypeInfo 注释只是为了尝试看看发生了什么。无论是否存在,我都会得到相同的行为。真正奇怪的是,我们在需要自定义反序列化的其他类中使用相同的基本功能,并且这些类可以很好地看到@class。这是它的层次结构:
@JsonAutoDetect(value = JsonMethod.NONE)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class TaggedObject implements Serializable {
...
}
然后我们有 DataObject
public abstract class DataObject extends TaggedObject {
...
}
,然后我们有 Join 类:
@JsonSerialize(using = Join.Serializer.class)
@JsonDeserialize(using = Join.Deserializer.class)
public abstract class Join<L, R> extends DataObject {
...
}
我认为 @JsonTypeInfo 将从 TaggedObject 继承,但在这里看不到它,而同一层次结构中的其他类可以看到它。
任何人对如何获取该对象的类以便创建适当的对象有任何想法吗?我在这里缺少什么?
I currently have a Join object that I am trying to write a custom deserializer for. We store the database key and the Java class name of the object in the JSON, and that looks something like this:
{
"@class": "com.mysite.data.ExpertFor",
"id": "expert-for-1",
"leftId": "user-profile-1",
"leftType": "com.mysite.data.UserProfile",
"rightId": "hotel-1",
"rightType": "com.mysite.data.Hotel"
}
We do this so we don't have to store the actual object in the database; rather, we retrieve it when we deserialize it. The issue is that my custom serializer cannot see the @class
element in the JSON stream - therefore, it cannot properly instantiate the needed object after all the data is read. Here is my deserializer:
public Join deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Map<String, Object> values = new HashMap<String, Object>();
// Loop through the JSON document and pull out all
// the values and put them into the map
JsonToken token = jp.nextToken();
while (token != JsonToken.END_OBJECT) {
logger.info("Non-data field: [" + token + "], name is [" + jp.getCurrentName() + "], text is [" + jp.getText() + "]");
if (token == JsonToken.VALUE_STRING) {
values.put(jp.getCurrentName(), jp.getText());
}
token = jp.nextToken();
}
// remainder of code omitted for brevity
}
Now, the Log4J lines that are written to the logs show all of the appropriate elements of the JSON I would expect, EXCEPT the @class
element
Data field: [VALUE_STRING], name is [id], text is [expert-for-1]
Data field: [VALUE_STRING], name is [leftId], text is [user-profile-1]
Data field: [VALUE_STRING], name is [rightId], text is [hotel-1]
Data field: [VALUE_STRING], name is [leftType], text is [com.mysite.data.UserProfile]
Data field: [VALUE_STRING], name is [rightType], text is [com.mysite.data.Hotel]
I have tried adding the @JsonTypeInfo
annotation to my class, like
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
but it appears the element is getting 'swallowed' by the code, and I don't know how to get at it. And I need this, because any descendants of Join
will need to be deserialized. Here is some additional information about the hierarchy this class is in:
Adding the @TypeInfo annotation was only there as an attempt to try and see what was going on. I am getting the same behavior whether that is there or not. What's really strange is that we are using this same basic functionality in other classes that need custom deserialization and those see the @class just fine. Here is the heirarchy of this:
@JsonAutoDetect(value = JsonMethod.NONE)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
public abstract class TaggedObject implements Serializable {
...
}
and then we have DataObject
public abstract class DataObject extends TaggedObject {
...
}
and then we have our Join class:
@JsonSerialize(using = Join.Serializer.class)
@JsonDeserialize(using = Join.Deserializer.class)
public abstract class Join<L, R> extends DataObject {
...
}
I would think the @JsonTypeInfo would be inherited from TaggedObject, but it is not seen here, whereas it is seen by other classes within the same hierarchy.
Anyone have any ideas on how I can grab the class of this object so I can create the appropriate object? What am I missing here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您不应该尝试手动处理 @class 并声明它由 Jackson 使用 @JsonTypeInfo 处理 - 要么让 Jackson 完全处理它(它可以并且将会这样做),或者删除注释并手动处理事情。
You should not try to manually handle @class AND declare it to be handled by Jackson with @JsonTypeInfo -- either let Jackson fully handle it (which it can and will do), or remove anntoation and handle things manually.
使用杰克逊树模型,以下代码打印丢失的
@class
:输出:
using Jackson Tree Model, the following code prints the missing
@class
:Output: