Right way to write JSON deserializer in Spring or extend it
I've searched a lot and the best way I've found so far is on this article:
Class to serialize
package net.sghill.example;import net.sghill.example.UserDeserializerimport net.sghill.example.UserSerializerimport org.codehaus.jackson.map.annotate.JsonDeserialize;import org.codehaus.jackson.map.annotate.JsonSerialize;@JsonDeserialize(using = UserDeserializer.class)public class User { private ObjectId id; private String username; private String password; public User(ObjectId id, String username, String password) { this.id = id; this.username = username; this.password = password; } public ObjectId getId() { return id; } public String getUsername() { return username; } public String getPassword() { return password; }}
Deserializer class
package net.sghill.example;import net.sghill.example.User;import org.codehaus.jackson.JsonNode;import org.codehaus.jackson.JsonParser;import org.codehaus.jackson.ObjectCodec;import org.codehaus.jackson.map.DeserializationContext;import org.codehaus.jackson.map.JsonDeserializer;import java.io.IOException;public class UserDeserializer extends JsonDeserializer<User> { @Override public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { ObjectCodec oc = jsonParser.getCodec(); JsonNode node = oc.readTree(jsonParser); return new User(null, node.get("username").getTextValue(), node.get("password").getTextValue()); }}
Edit:Alternatively you can look at this article which uses new versions of com.fasterxml.jackson.databind.JsonDeserializer.
I was trying to @Autowire
a Spring-managed service into my Deserializer
. Somebody tipped me off to Jackson using the new
operator when invoking the serializers/deserializers. This meant no auto-wiring of Jackson's instance of my Deserializer
. Here's how I was able to @Autowire
my service class into my Deserializer
:
context.xml
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper" ref="objectMapper" /> </bean> </mvc:message-converters></mvc><bean id="objectMapper" class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <!-- Add deserializers that require autowiring --> <property name="deserializersByType"> <map key-type="java.lang.Class"> <entry key="com.acme.Anchor"> <bean class="com.acme.AnchorDeserializer" /> </entry> </map> </property></bean>
Now that my Deserializer
is a Spring-managed bean, auto-wiring works!
AnchorDeserializer.java
public class AnchorDeserializer extends JsonDeserializer<Anchor> { @Autowired private AnchorService anchorService; public Anchor deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException { // Do stuff }}
AnchorService.java
@Servicepublic class AnchorService {}
Update: While my original answer worked for me back when I wrote this, @xi.lin's response is exactly what is needed. Nice find!
With Spring MVC 4.2.1.RELEASE, you need to use the new Jackson2 dependencies as below for the Deserializer to work.
Dont use this
<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.12</version> </dependency>
Use this instead.
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.2.2</version> </dependency>
Also use com.fasterxml.jackson.databind.JsonDeserializer
and com.fasterxml.jackson.databind.annotation.JsonDeserialize
for the deserialization and not the classes from org.codehaus.jackson