8

I want to run some custom code when deserializing a particular type with Jackson 1.9. However, I don't want to hand-write the whole deserializer, just add to the default behaviour. Trying to do this in a JsonDeserializer the naive way results in infinite recursion, however. Currently, my approach uses a completely separate ObjectMapper, but that feels like a huge hack.

private static class SpecialDeserializer extends JsonDeserializer<Special> {
    @Override
    public Special deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        JsonNode jsonNode = jp.readValueAsTree();
        ObjectMapper otherMapper = getOtherMapper();
        Special special = otherMapper.readValue(jsonNode, Special.class);
        special.setJsonNode(jsonNode);
        return special;
    }
}
Tavian Barnes
  • 12,477
  • 4
  • 45
  • 118
  • Does the code have anything to do with the deserialization? – Sotirios Delimanolis Jan 31 '14 at 00:32
  • Yes, sadly. I'm saving the JsonNode representation for later use if I need to re-map to a more derived type – Tavian Barnes Jan 31 '14 at 00:34
  • If you want a "more derived type", wouldn't polymorphic deserialization be the way to go? Jackson handles this natively. – Paul Hicks Feb 04 '14 at 00:31
  • It would be but I don't control the input format, so I can't add the type hints Jackson would use. Not all the consumers are using Jackson either. – Tavian Barnes Feb 04 '14 at 00:34
  • Does the input format contain enough information for Jackson to infer a type? That is, is there a property specific to each derived type, or a value of a shared property that you can discriminate on? – Paul Hicks Feb 04 '14 at 00:38
  • I could conceivably use a shared property. I'm still curious about the question as asked though, it seems like it should be easy to call the reflective deserializer from a custom one. – Tavian Barnes Feb 04 '14 at 00:58
  • I'm pretty sure your "hack" is the correct way to do what you are asking. However you didn't state your reason for trying to do this, and there is likely a much better (cleaner, less hackish) solution for what you are really trying to do (what you hope to get as the eventual end result). – Shadow Man Feb 06 '14 at 19:53
  • Yeah I do that already, hence `getOtherMapper()` in my example. Well, glad to know I'm not missing something obvious. To me this is sort of like `Result result = super.deserialize(...); result.doSomething(); return result;` so I'm still surprised I need two ObjectMappers. – Tavian Barnes Feb 06 '14 at 20:01

2 Answers2

2

Polymorphic deserialization should be able to handle deriving the type of the input from the content of the input. If that's what you need the custom deserialization for, you can achieve it via annotations. See this example for reference.

(Apologies for not answering the actual question; this answers what I'm inferring to the "root" question, from the poster's comment).

Paul Hicks
  • 13,289
  • 5
  • 51
  • 78
  • This seems to be the "correct" way to do what I'm trying to accomplish, so I gave you the bounty. But if someone has a direct answer to the question as asked (or convincing evidence that it can't be done) I'll give out another bounty. – Tavian Barnes Feb 10 '14 at 15:40
1

If you want to implement this special behavior only for one class, you can create additional type with the same properties set. I am not sure that this is a real answer for your question, but see my below example. POJO class with property:

@JsonDeserialize(using = SpecialJsonDeserializer.class)
class Special {

    private String name;

    //getters, setters, toString
}

Deserializer with inner type:

class SpecialJsonDeserializer extends JsonDeserializer<Special> {
    @Override
    public Special deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        InnerSpecial innerSpecial = jp.readValueAs(InnerSpecial.class);

        return innerSpecial.createRealSpecial();
    }

    private static class InnerSpecial {

        public String name;

        public Special createRealSpecial() {
            Special special = new Special();
            // copy other properties, add additional code
            special.setName(name);

            return special;
        }
    }
}

And simple usage:

public class JsonProgram {

    public static void main(String[] args) throws IOException {
        Special special = new Special();
        special.setName("ye");

        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(special);
        System.out.println(json);
        System.out.println(mapper.readValue(json, Special.class));
    }
}

I think, this is a little bit less hack than yours but it is still hack.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146