Tatu,
Thanks for the reply. DeserializationContext was really the piece I was missing. I was able to get it working with just 1 smallish hack. I'll lay out what I did here for Luis, and anyone else that might be looking for something similar, with some pseudo code for the Animal example in my original post. Feel free to let me know if there are better ways to do what I've done.
1. Added my own DeserializerModifier and used it to inject decorators for the built-in deserializers
public class TemplateDeserializerModifer {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
final JsonDeserializer<?> modified = super.modifyDeserializer(config, beanDesc, deserializer);
if(Template.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new TemplatesDeserializer((JsonDeserializer<Templates>) modified);
}else if(Animal.class.isAssignableFrom(beanDesc.getBeanClass()) && modified instanceof BeanDeserializer) {
return new AnimalDeserializer((BeanDeserializer) modified);
}else{
return modified;
}
}
}
2. Attach my templates to the DeserializationContext in TemplatesDeserializer
public class TemplatesDeserializer extends StdDeserializer<Templates> implements ResolvableDeserializer {
public static final String TEMPLATES_KEY_NAME = "templates";
private final JsonDeserializer<Templates> delegate;
protected TemplatesDeserializer(JsonDeserializer<Templates> delegate) {
super(Templates.class);
this.delegate = delegate;
}
@Override
public Templates deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Templates templates = delegate.deserialize(p, ctxt);
ctxt.setAttribute(TEMPLATES_KEY_NAME, templates);
return templates;
}
@Override
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
if(delegate instanceof ResolvableDeserializer) {
((ResolvableDeserializer) delegate).resolve(ctxt);
}
}
}
3. In AnimalDeserializer, leverage the templates now available through the DeserializationContext
public class AnimalDeserializer extends BeanDeserializer implements ResolvableDeserializer {
public AnimalDeserializer(BeanDeserializer originalDeserializer) {
super(originalDeserializer);
}
@Override
public Animal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
Animal animal = (Animal) super.deserialize(p, ctxt);
Templates templates = (Templates) ctxt.getAttribute(TemplatesDeserializer.TEMPLATES_KEY_NAME);
Template template = getTemplate(animal, templates);
for(Map.Entry<String, String> entry : template.getProperties() {
String propName = entry.getKey();
String propValue = entry.getValue();
SettableBeanProperty prop = _beanProperties.find(propName);
if (prop != null) { // normal case
try {
prop.deserializeAndSet(new TemplateParser(propValue), ctxt, tag);
} catch (Exception e) {
wrapAndThrow(e, tag, propName, ctxt);
}
}
}
return tag;
}
}
The hack I referred to earlier is there where I create an instance of TemplateParser. Template parser is a new sub-class of JsonParser that exists solely to provide the data necessary for BeanDeserializer to parser a few more properties from somewhere other than the parser it's already got. Most of the necessary method implementations in that new class are NO-OP. The only ones that matter looks about like this:
public class TemplateParser extends JsonParser {
private final String template;
TemplateParser(String template) {
this.template = template;
}
@Override
public JsonToken getCurrentToken() {
return JsonToken.VALUE_STRING;
}
@Override
public String getText() throws IOException {
return template;
}
}
Hopefully all that's not too big a hack. It does exactly what I was hoping for.
Thanks,
Hob