2016-05-08 2 views
1

Вот (часть) ответа сервера:Джексон спутать поле объекта с JsonTypeInfo.Id.NAME

{ 
    "ok": true, 
    "result": [ 
    { 
     "update_id": 489881706, 
     "message": { 
     "message_id": 5, 
     "from": { 
      "id": 188474643, 
      "first_name": "Alireza", 
      "last_name": "Mohamadi", 
      "username": "SuNova" 
     }, 
     "chat": { 
      "id": 188474643, 
      "first_name": "Alireza", 
      "last_name": "Mohamadi", 
      "username": "SuNova", 
      "type": "private" 
     }, 
     "date": 1462608191, 
     "text": "1" 
     } 
    } 
    ] 
} 

Я создал 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() //... 

} 

И у меня есть 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?

+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, потому что Джексон даже не думает, что эта часть от TObject. Он попытается найти TObject 1 слой ниже в json. Если вы не можете изменить свой json, вы должны полностью перестроить свои классы или создать собственный десериализатор.

Я сделал небольшой демо, как вы можете использовать @JsonAnySetter и @JsonAnyGetter здесь: https://gist.github.com/varren/766b6830878c9e15c51784f57b303646#file-jsonanygetter-and-jsonanysetter

Пользовательские JsonDeserializer и JsonSerializer для класса обновления в этой демонстрации может также работать. 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(); 
    } 
} 
+0

Спасибо! Мне нравится читать ваш полный ответ, и я буду отмечать его как ответ, когда получаю каждый аспект, потому что это не обычный ответ и больше похож на лекцию. В любом случае у меня есть некоторые вопросы. 1st: Почему вы использовали '@ JsonIgnore' в полях' TObject'? Разве это не заставляет Джексона игнорировать геттер и сеттер для 'update_id'? 2nd: Почему вы использовали '@ JsonProperty' в каждом поле, в то время как их можно было распознать даже без аннотации? –

+1

@AlirezaMohamadi 1) Я использую пользовательский 'JsonSerializer', и я не хочу сериализовать updateId внутри сообщения. Я хочу получить '{" update_id ": 1," message "= {}}', но если я не проигнорирую update_id, я получу '' message "= {" update_id ": 1}', поэтому i manualy insert 'update_id' на том же lvl с' message' в сериализаторе. и предотвращать сериализацию по умолчанию с помощью @JsonIgnore 2) я использовал http://www.jsonschema2pojo.org/ для создания всех POJO. и, как вы видите, иногда имена полей немного отличаются от '@JsonProperty (" message_id ") public Integer messageId;' но конечно, вы можете использовать любой подход, который вам нравится здесь. – varren

+1

@AlirezaMohamadi btw i сделал предположение о том, что ваши объекты TObject jsons имеют такую ​​структуру: '{" update_id ": 1," message ": {...}}' или '{" update_id ": 1," chat ": { ...}} 'или' {"update_id": 1, "message_entity": {...}} 'так что если это не то, что вы просили, то я, вероятно, должен обновить свой пост) – varren

Смежные вопросы