Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot inject objects into a @JsonDeserialize converter. #685

Closed
drekka opened this issue Jan 22, 2015 · 3 comments
Closed

Cannot inject objects into a @JsonDeserialize converter. #685

drekka opened this issue Jan 22, 2015 · 3 comments

Comments

@drekka
Copy link

drekka commented Jan 22, 2015

I have a class that I'm using to convert a part of the incoming JSON data to various data types.

public class RatesTableConverter extends StdConverter<Map<String, BigDecimal>, List<Rate>> {
    ...
}

AS part of doing this I need to be able to look up an object in my Spring application context. My initial attempt was to simply add:

@Autowired
private MyObjClass myObj;

But this didn't work and myObj was null. So i did lots of reading and tried this in the RatesTableConverter as suggested on one site:

@Override
 public List<Rate> convert(Map<String, BigDecimal> ratesTable) {
     SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
   ...
}

This should have located the application context and processed the spring annotations. However this also did not work. Perhaps I missed something. I don't know.

So next I decided to try Jackson's @JacksonInject like this:

@JacksonInject
private MyObjClass myObj;

And modified my ObjectMapper like this (Note this is being created by the spring config):

@Autowired
private MyObjClass myObj;

@Bean
public ObjectMapper jsonObjectMapper() {

    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    ObjectMapper mapper = converter.getObjectMapper();
    mapper.setInjectableValues(new InjectableValues.Std().addValue(MyObjClass.class, this.myObj));
    return mapper;
}

Note that MyObjClass is actually an abstract of 'MyObjClassImpl which is the actual instantiated type. I don't think that has anything to do with those though.

Alas though, this did not work either. I added lots of debug points to the Jackson codebase and as far as I can figure out, it appears that when a converter is instantiated for handling JSON conversions, it is not processed for @JacksonInject annotations. I also don't know why the spring auto wiring failed as the stack traces looked like they should have worked.

So this is either a bug, missing feature or a request for a new feature.

Thanks.

@cowtowncoder
Copy link
Member

Injection only works for value objects bound from JSON, not for handler objects. So it is not a bug in that sense.

This can be considered a feature request then. However, implementing would be problematic as the life-cycle of handlers is different from that of values: handlers are created once, shared, and therefore per-operation injection is impossible the way things are currently.

However, solving the underlying problem itself is not necessarily difficult, using another existing facility: attributes. Accessing attributes from within deserializer is easy (getAttribute(), setAttribute()); setting them via ObjectReader can be done using withAttribute(...) (or withAttributes()) call.

You could also directly access injectable values from deserializer, but there isn't much benefit from doing that.

One downside is that Converter does not have access to DeserializationContext. So instead of plugging converter, it is necessary to use a custom deserializer.

@drekka
Copy link
Author

drekka commented Jan 22, 2015

If I understand this correctly you are suggesting I look into using a custom deserialer. I originally looked at this, however my understanding is that custom deserialisers are run against the entire JSON content. I'm only interested in a small part of the message where I want to re-arrange the incoming data into a structure that best matches my needs. Hence the application of a converter on a specific field.

Is there a factory class in Jackson that handles the creation of the converter? Somewhere I can extend and override to enhance the creation process?

Thanks

@cowtowncoder
Copy link
Member

No, custom deserializers/serializers are not run against the entire JSON content. They are given access to the current position, and they may choose to do handle everything; but better way to do it is to delegate, and this is how Jackson's default (de)serializers do it.
But be that as it may; I agree in that writing custom (de)serializer is not fun, and converters are in general a better way to go about it.

So: yes, you can implement your own HandlerInstantiator, override converterInstance(), and set handler via ObjectMapper.setHandlerInstantiator(...). That should allow you to do necessary initialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants