杰克逊用JsonTypeInfo.Id.NAME误认对象字段

时间:2016-05-08 07:42:44

标签: java json annotations jackson polymorphism

以下是服务器响应的一部分:

Result

我创建了一个public class Result { private TObject[] result; private boolean ok; public void setOk (boolean ok) //Rest of the code public void setResult (TObject[] result) //... public TObject[] getResult () //... public boolean getOk () //... } 类,它类似于(Trimmed to reduce code):

Abstract Class

我有TObject名为@JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) @JsonSubTypes ({ @JsonSubTypes.Type (name = "chat", value = Chat.class), @JsonSubTypes.Type (name = "message", value = Message.class), @JsonSubTypes.Type (name = "message_entity", value = MessageEntity.class), }) public abstract class TObject { int update_id; boolean isUpdate; public void setUpdate_id (int update_id) { this.update_id = update_id; isUpdate = true; } public int getUpdate_id () { return update_id; } }

@JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)

问题是,因为我使用了

update_id

它没有将JsonTypeInfo.Id.NAME识别为字段,而是将其识别为com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id 'update_id' into a subtype of [simple type, class org.telegram.objects.TObject]: known type ids = [TObject, chat, message, message_entity] 并向我显示:

update_id

现在请告诉我如何告诉杰克逊将TObject解析为JsonTypeInfo.Id.NAME字段并且不要将其混淆为<nav class="navbar navbar-default navbar-fixed-top"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Brand</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li> <li><a href="#">Link</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> <li role="separator" class="divider"></li> <li><a href="#">One more separated link</a></li> </ul> </li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">Link</a></li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav>

1 个答案:

答案 0 :(得分:1)

我会试着解释这里发生了什么。那么让我们来看看你的注释: 杰克逊希望Result拥有这种格式。 { "ok": true, "result": []}这部分是正确的。但TObject

@JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)

JsonTypeInfo.As.WRAPPER_OBJECT表示杰克逊会尝试在结果数组中找到{"chat":{}}{"message":{}}{"message_entity":{}}。这是演示:

{ "ok": true, "result": [{"chat":{}}, {"message_entity":{}}, {"message":{}}]} 

但是你在json中的第一个元素实际上是update_id。杰克逊找不到任何update_id类型,这就是为什么他只是停止解析并抛出异常。所以回答你的问题:how can I tell jackson to parse update_id as a field of TObject and don't confuse it as JsonTypeInfo.Id.NAME无法使用JsonTypeInfo ,因为jackson甚至不认为这部分来自TObject。他将尝试在json中找到TObject 1层。如果你不能改变你的json,你必须完全重构你的类或创建自定义反序列化器。

我在这里制作了一个小型演示如何使用@JsonAnySetter@JsonAnyGetterhttps://gist.github.com/varren/766b6830878c9e15c51784f57b303646#file-jsonanygetter-and-jsonanysetter

此演示中的Update类的自定义JsonDeserializerJsonSerializer也可以使用。 https://gist.github.com/varren/766b6830878c9e15c51784f57b303646#file-custom-deserializer-and-serializer

只需删除所有typeinfo注释并添加

即可
private static class TObjectDeserializer extends JsonDeserializer<TObject> {
    @Override
    public TObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode tree = p.readValueAsTree();
        tree.get("chat");
        TObject resultValue = null;
        if(tree.has("chat")){
            resultValue = p.getCodec().treeToValue(tree.get("chat"), Chat.class);
        }else if(tree.has("message")){
            resultValue = p.getCodec().treeToValue(tree.get("message"),Message.class);
        } else if(tree.has("message_entity")){
            resultValue = p.getCodec().treeToValue(tree.get("message_entity"),MessageEntity.class);
        }
        if(resultValue!= null)
            resultValue.setUpdateId(tree.get("update_id").asInt());
        return resultValue;
    }
}

private static class TObjectSerializer extends JsonSerializer<TObject> {
    private static ObjectMapper mapper = new ObjectMapper();
    @Override
    public void serialize(TObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartObject();

        gen.writeNumberField("update_id", value.getUpdateId());
        String key = "";

        if(value instanceof Chat){
            key = "chat";
        }else if(value instanceof Message){
            key =  "message";
        } else if(value instanceof MessageEntity){
            key = "message_entity";
        }
        gen.writeFieldName(key);
        gen.writeRawValue(mapper.writeValueAsString(value));

        gen.writeEndObject();
    }
}