2

Given

public class ConstraintMatch {
    protected String constraintName;
    protected Score score;
    ...
}

I have the following serializer in Jackson:

public class ConstraintMatchJacksonJsonSerializer extends JsonSerializer<ConstraintMatch> {

    @Override
    public void serialize(ConstraintMatch constraintMatch, JsonGenerator generator, SerializerProvider serializers)
            throws IOException {
        generator.writeStartObject();
        generator.writeStringField("constraintName", constraintMatch.getConstraintName());
        generator.writeFieldName("score");
        // Delegate to serialization to the default Score serializer
        serializers.findValueSerializer(Score.class)
                 .serialize(constraintMatch.getScore(), generator, serializers);
        generator.writeEndObject();
    }

}

How do I write a deserializer that also delegates to the default deserializer?

public class ConstraintMatchJacksonJsonDeserializer extends JsonDeserializer<ConstraintMatch> {

    @Override
    public ConstraintMatch deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        JsonNode tree = parser.readValueAsTree();

        String constraintName = tree.get("constraintName").asText();

        JsonNode scoreNode = tree.get("score");
        Score score = ...; // How do I delegate to the default deserializer?
        return new ConstraintMatch(constraintName, score);
    }

}

I've looked at findContextualValueDeserializer() etc, but I can't create a BeanProperty instance.

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120

2 Answers2

1

In a similar situation, I actually found there were two problems to solve. Firstly, as you say, the need to delegate back to the normal deserializer. But the other problem I encountered was how to feed the JsonNode (TreeNode below) into that next deserialize(JsonParser, ...).

The following is a working sample from that situation, where I wanted to do a lookahead to figure out the subclass.

Hopefully the node here is your scoreNode. And it sounds like objectClass is just Score.class for you.

@Override
public T deserialize(JsonParser parser, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    ObjectMapper mapper = (ObjectMapper) parser.getCodec();
    TreeNode node = parser.readValueAsTree();

    // Select the subclass to deserialize as
    Class<? extends T> objectClass = deduceClass(node);

    // This based on ObjectMapper._convert()
    // - the problem here was the JsonParser (parser) had gone past the current node
    TokenBuffer buf = new TokenBuffer(mapper, false);
    SerializationConfig config = mapper.getSerializationConfig()
            .without(SerializationFeature.WRAP_ROOT_VALUE);
    DefaultSerializerProvider serializerProvider = ((DefaultSerializerProvider) mapper
            .getSerializerProvider()).createInstance(config,
                    mapper.getSerializerFactory());
    serializerProvider.serializeValue(buf, node);
    JsonParser nestedParser = buf.asParser();
    nestedParser.nextToken();
    JsonDeserializer<Object> deserializer = ctxt
            .findRootValueDeserializer(
                    mapper.getTypeFactory().constructType(objectClass));
    @SuppressWarnings("unchecked")
    T obj = (T) deserializer.deserialize(nestedParser, ctxt);

    return obj;

}

(Just in case, this was with Jackson 2.7.9)

I'd be pleased to hear about a simpler way to create a JsonParser from a node.

df778899
  • 10,703
  • 1
  • 24
  • 36
1

Serializing this:

    constraintMatch.getConstraintPackage());
    generator.writeStringField("constraintName", constraintMatch.getConstraintName());

    generator.writeFieldName("score");
    // Delegate to PolymorphicScoreJacksonJsonSerializer
    JsonSerializer<Object> scoreSerializer = serializers.findValueSerializer(Score.class);
    scoreSerializer.serialize(constraintMatch.getScore(), generator, serializers);
    generator.writeEndObject();

Can be deserialized with this:

    parser.nextToken();
    if (!"constraintName".equals(parser.getCurrentName())) {
        throw new IllegalStateException(...);
    }
    parser.nextToken();
    String constraintName = parser.getValueAsString();

    parser.nextToken();
    if (!"score".equals(parser.getCurrentName())) {
        throw new IllegalStateException(...);
    }
    parser.nextToken();
    JsonDeserializer<Object> scoreDeserializer = context.findNonContextualValueDeserializer(context.constructType(Score.class));
    Score score = (Score) scoreDeserializer.deserialize(parser, context);
Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120