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

时间:2021-03-20 18:01:37

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:

我目前有一个Join对象,我正在尝试编写自定义反序列化器。我们在JSON中存储数据库键和对象的Java类名,看起来像这样:

{
    "@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:

我们这样做,所以我们不必将实际对象存储在数据库中;相反,我们在反序列化时检索它。问题是我的自定义序列化程序无法在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
}    

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

现在,写入日志的Log4J行显示了我期望的JSON的所有适当元素,除了@class元素

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注释添加到我的类中,比如

@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:

但似乎元素被代码“吞噬”了,我不知道该怎么做。我需要这个,因为Join的任何后代都需要反序列化。以下是有关此类所在层次结构的一些其他信息:

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:

添加@TypeInfo注释只是试图尝试查看正在发生的事情。无论是否存在,我都会得到相同的行为。真正奇怪的是,我们在其他需要自定义反序列化的类中使用相同的基本功能,那些看到@class就好了。这是这个的层次结构:

@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

然后我们有DataObject

public abstract class DataObject extends TaggedObject {
...
}

and then we have our Join class:

然后我们有Join类:

@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.

我认为@JsonTypeInfo将继承自TaggedObject,但是在这里看不到它,而同一层次结构中的其他类可以看到它。

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?

任何人对如何抓取这个对象的类有任何想法,所以我可以创建适当的对象?我在这里想念的是什么?

2 个解决方案

#1


0  

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并声明它由Jackson与@JsonTypeInfo处理 - 要么让Jackson完全处理它(它可以做和将要做),或者删除anntoation并手动处理事情。

#2


0  

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

使用Jackson Tree Model,以下代码打印缺少的@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]

#1


0  

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并声明它由Jackson与@JsonTypeInfo处理 - 要么让Jackson完全处理它(它可以做和将要做),或者删除anntoation并手动处理事情。

#2


0  

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

使用Jackson Tree Model,以下代码打印缺少的@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]