View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.http.converter.json;
18  
19  import java.io.IOException;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Map;
23  
24  import com.fasterxml.jackson.annotation.JsonTypeInfo;
25  import com.fasterxml.jackson.core.JsonGenerator;
26  import com.fasterxml.jackson.core.JsonParser;
27  import com.fasterxml.jackson.core.JsonProcessingException;
28  import com.fasterxml.jackson.core.ObjectCodec;
29  import com.fasterxml.jackson.databind.DatabindContext;
30  import com.fasterxml.jackson.databind.DeserializationConfig;
31  import com.fasterxml.jackson.databind.DeserializationContext;
32  import com.fasterxml.jackson.databind.JavaType;
33  import com.fasterxml.jackson.databind.JsonDeserializer;
34  import com.fasterxml.jackson.databind.JsonNode;
35  import com.fasterxml.jackson.databind.JsonSerializer;
36  import com.fasterxml.jackson.databind.KeyDeserializer;
37  import com.fasterxml.jackson.databind.ObjectMapper;
38  import com.fasterxml.jackson.databind.SerializationConfig;
39  import com.fasterxml.jackson.databind.SerializerProvider;
40  import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
41  import com.fasterxml.jackson.databind.annotation.JsonSerialize;
42  import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
43  import com.fasterxml.jackson.databind.annotation.JsonTypeResolver;
44  import com.fasterxml.jackson.databind.jsontype.NamedType;
45  import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
46  import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
47  import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
48  import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
49  import com.fasterxml.jackson.databind.type.TypeFactory;
50  import org.junit.Before;
51  import org.junit.Test;
52  
53  import org.springframework.beans.factory.annotation.Autowired;
54  import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
55  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
56  import org.springframework.beans.factory.support.RootBeanDefinition;
57  
58  import static org.junit.Assert.*;
59  
60  /**
61   * Test class for {@link SpringHandlerInstantiatorTests}.
62   *
63   * @author Sebastien Deleuze
64   */
65  public class SpringHandlerInstantiatorTests {
66  
67  	private SpringHandlerInstantiator instantiator;
68  
69  	private ObjectMapper objectMapper;
70  
71  
72  	@Before
73  	public void setup() {
74  		DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
75  		AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
76  		bpp.setBeanFactory(bf);
77  		bf.addBeanPostProcessor(bpp);
78  		bf.registerBeanDefinition("capitalizer", new RootBeanDefinition(Capitalizer.class));
79  		instantiator = new SpringHandlerInstantiator(bf);
80  		objectMapper = Jackson2ObjectMapperBuilder.json().handlerInstantiator(instantiator).build();
81  	}
82  
83  
84  	@Test
85  	public void autowiredSerializer() throws JsonProcessingException {
86  		User user = new User("bob");
87  		String json = this.objectMapper.writeValueAsString(user);
88  		assertEquals("{\"username\":\"BOB\"}", json);
89  
90  	}
91  
92  	@Test
93  	public void autowiredDeserializer() throws IOException {
94  		String json = "{\"username\":\"bob\"}";
95  		User user = this.objectMapper.readValue(json, User.class);
96  		assertEquals(user.getUsername(), "BOB");
97  	}
98  
99  	@Test
100 	public void autowiredKeyDeserializer() throws IOException {
101 		String json = "{\"credentials\":{\"bob\":\"admin\"}}";
102 		SecurityRegistry registry = this.objectMapper.readValue(json, SecurityRegistry.class);
103 		assertTrue(registry.getCredentials().keySet().contains("BOB"));
104 		assertFalse(registry.getCredentials().keySet().contains("bob"));
105 	}
106 
107 	@Test
108 	public void applicationContextAwaretypeResolverBuilder() throws JsonProcessingException {
109 		this.objectMapper.writeValueAsString(new Group("authors"));
110 		assertTrue(CustomTypeResolverBuilder.isAutowiredFiledInitialized);
111 	}
112 
113 	@Test
114 	public void applicationContextAwareTypeIdResolver() throws JsonProcessingException {
115 		this.objectMapper.writeValueAsString(new Group("authors"));
116 		assertTrue(CustomTypeIdResolver.isAutowiredFiledInitialized);
117 	}
118 
119 
120 	public static class UserDeserializer extends JsonDeserializer<User> {
121 
122 		@Autowired
123 		private Capitalizer capitalizer;
124 
125 		@Override
126 		public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws  IOException {
127 			ObjectCodec oc = jsonParser.getCodec();
128 			JsonNode node = oc.readTree(jsonParser);
129 			return new User(this.capitalizer.capitalize(node.get("username").asText()));
130 		}
131 	}
132 
133 
134 	public static class UserSerializer extends JsonSerializer<User> {
135 
136 		@Autowired
137 		private Capitalizer capitalizer;
138 
139 		@Override
140 		public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
141 			jsonGenerator.writeStartObject();
142 			jsonGenerator.writeStringField("username", this.capitalizer.capitalize(user.getUsername()));
143 			jsonGenerator.writeEndObject();
144 		}
145 	}
146 
147 
148 	public static class UpperCaseKeyDeserializer extends KeyDeserializer {
149 
150 		@Autowired
151 		private Capitalizer capitalizer;
152 
153 		@Override
154 		public Object deserializeKey(String key, DeserializationContext context) throws IOException, JsonProcessingException {
155 			return this.capitalizer.capitalize(key);
156 		}
157 	}
158 
159 
160 	public static class CustomTypeResolverBuilder extends StdTypeResolverBuilder {
161 
162 		@Autowired
163 		private Capitalizer capitalizer;
164 
165 		public static boolean isAutowiredFiledInitialized = false;
166 
167 		@Override
168 		public TypeSerializer buildTypeSerializer(SerializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
169 			isAutowiredFiledInitialized = (this.capitalizer != null);
170 			return super.buildTypeSerializer(config, baseType, subtypes);
171 		}
172 
173 		@Override
174 		public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) {
175 			return super.buildTypeDeserializer(config, baseType, subtypes);
176 		}
177 	}
178 
179 
180 	public static class CustomTypeIdResolver implements TypeIdResolver {
181 
182 		@Autowired
183 		private Capitalizer capitalizer;
184 
185 		public static boolean isAutowiredFiledInitialized = false;
186 
187 		public CustomTypeIdResolver() {
188 		}
189 
190 		@Override
191 		public String idFromValueAndType(Object o, Class<?> type) {
192 			return type.getClass().getName();
193 		}
194 
195 		@Override
196 		public JsonTypeInfo.Id getMechanism() {
197 			return JsonTypeInfo.Id.CUSTOM;
198 		}
199 
200 		@Override
201 		public JavaType typeFromId(String s) {
202 			return TypeFactory.defaultInstance().constructFromCanonical(s);
203 		}
204 
205 		@Override
206 		public String idFromValue(Object value) {
207 			isAutowiredFiledInitialized = (this.capitalizer != null);
208 			return value.getClass().getName();
209 		}
210 
211 		@Override
212 		public void init(JavaType type) {
213 		}
214 
215 		@Override
216 		public String idFromBaseType() {
217 			return null;
218 		}
219 
220 		// New in Jackson 2.5
221 		public JavaType typeFromId(DatabindContext context, String id) {
222 			return null;
223 		}
224 	}
225 
226 
227 	@JsonDeserialize(using = UserDeserializer.class)
228 	@JsonSerialize(using = UserSerializer.class)
229 	public static class User {
230 
231 		private String username;
232 
233 		public User() {
234 		}
235 
236 		public User(String username) {
237 			this.username = username;
238 		}
239 
240 		public String getUsername() { return this.username; }
241 	}
242 
243 
244 	public static class SecurityRegistry {
245 
246 		@JsonDeserialize(keyUsing = UpperCaseKeyDeserializer.class)
247 		private Map<String, String> credentials = new HashMap<>();
248 
249 		public void addCredential(String username, String credential) {
250 			this.credentials.put(username, credential);
251 		}
252 
253 		public Map<String, String> getCredentials() {
254 			return credentials;
255 		}
256 	}
257 
258 
259 	@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "type")
260 	@JsonTypeResolver(CustomTypeResolverBuilder.class)
261 	@JsonTypeIdResolver(CustomTypeIdResolver.class)
262 	public static class Group {
263 
264 		private String name;
265 
266 		public Group(String name) {
267 			this.name = name;
268 		}
269 
270 		public Group() {
271 		}
272 
273 		public String getType() {
274 			return Group.class.getName();
275 		}
276 	}
277 
278 
279 	public static class Capitalizer {
280 
281 		public String capitalize(String text) {
282 			return text.toUpperCase();
283 		}
284 	}
285 
286 }