自定义 Jackson 解串器无法确定目标类别

发布于 2024-12-08 06:40:48 字数 2709 浏览 0 评论 0原文

我目前有一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱情眠于流年 2024-12-15 06:40:48

您不应该尝试手动处理 @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.

何必那么矫情 2024-12-15 06:40:48

使用杰克逊树模型,以下代码打印丢失的 @class

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonString);
Iterator<String> iterator = root.fieldNames();
while (iterator.hasNext()) {
    String fieldName = iterator.next();
    String fieldValue = root.get(fieldName).asText();
    System.out.println("name is [" + fieldName + "], text is [" + fieldValue + "]");
}

输出:

name is [@class], text is [com.mysite.data.ExpertFor]
name is [id], text is [expert-for-1]
name is [leftId], text is [user-profile-1]
name is [leftType], text is [com.mysite.data.UserProfile]
name is [rightId], text is [hotel-1]
name is [rightType], text is [com.mysite.data.Hotel]

using Jackson Tree Model, the following code prints the missing @class:

ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(jsonString);
Iterator<String> iterator = root.fieldNames();
while (iterator.hasNext()) {
    String fieldName = iterator.next();
    String fieldValue = root.get(fieldName).asText();
    System.out.println("name is [" + fieldName + "], text is [" + fieldValue + "]");
}

Output:

name is [@class], text is [com.mysite.data.ExpertFor]
name is [id], text is [expert-for-1]
name is [leftId], text is [user-profile-1]
name is [leftType], text is [com.mysite.data.UserProfile]
name is [rightId], text is [hotel-1]
name is [rightType], text is [com.mysite.data.Hotel]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文