View Javadoc
1   package com.buckosoft.PicMan.service.support;
2   
3   import java.io.IOException;
4   import java.lang.reflect.Type;
5   import java.util.*;
6   
7   import com.fasterxml.jackson.core.*;
8   import com.fasterxml.jackson.databind.*;
9   import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
10  import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
11  import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper;
12  import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
13  import com.fasterxml.jackson.databind.node.ObjectNode;
14  import com.fasterxml.jackson.databind.ser.ContainerSerializer;
15  import com.fasterxml.jackson.databind.ser.ContextualSerializer;
16  import com.fasterxml.jackson.databind.ser.impl.PropertySerializerMap;
17  import com.fasterxml.jackson.databind.type.TypeFactory;
18  
19  /**
20   * Standard serializer implementation for serializing {link java.util.Map}
21   * types.
22   * <p>
23   * Note: about the only configurable setting currently is ability to filter out
24   * entries with specified names.
25   */
26  @JacksonStdImpl
27  public class KeyValueMapSerializer extends ContainerSerializer<Map<?, ?>>
28  		implements ContextualSerializer, MapLabels {
29  	protected final static JavaType UNSPECIFIED_TYPE = TypeFactory
30  			.unknownType();
31  
32  	/**
33  	 * Map-valued property being serialized with this instance
34  	 */
35  	protected final BeanProperty _property;
36  
37  	/**
38  	 * Set of entries to omit during serialization, if any
39  	 */
40  	protected final HashSet<String> _ignoredEntries;
41  
42  	/**
43  	 * Whether static types should be used for serialization of values or not
44  	 * (if not, dynamic runtime type is used)
45  	 */
46  	protected final boolean _valueTypeIsStatic;
47  
48  	/**
49  	 * Declared type of keys
50  	 */
51  	protected final JavaType _keyType;
52  
53  	/**
54  	 * Declared type of contained values
55  	 */
56  	protected final JavaType _valueType;
57  
58  	/**
59  	 * Key serializer to use, if it can be statically determined
60  	 */
61  	protected JsonSerializer<Object> _keySerializer;
62  
63  	/**
64  	 * Type identifier serializer used for keys, if any.
65  	 */
66  	protected final TypeSerializer _keyTypeSerializer;
67  
68  	/**
69  	 * Value serializer to use, if it can be statically determined
70  	 */
71  	protected JsonSerializer<Object> _valueSerializer;
72  
73  	/**
74  	 * Type identifier serializer used for values, if any.
75  	 */
76  	protected final TypeSerializer _valueTypeSerializer;
77  
78  	/**
79  	 * If value type can not be statically determined, mapping from runtime
80  	 * value types to serializers are stored in this object.
81  	 */
82  	protected PropertySerializerMap _dynamicValueSerializers;
83  
84  	/*
85  	 * /********************************************************** /* Life-cycle
86  	 * /**********************************************************
87  	 */
88  
89  	@SuppressWarnings("unchecked")
90  	protected KeyValueMapSerializer(HashSet<String> ignoredEntries,
91  			JavaType keyType, JavaType valueType, boolean valueTypeIsStatic,
92  			TypeSerializer kts, TypeSerializer vts,
93  			JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer) {
94  		super(Map.class, false);
95  		_ignoredEntries = ignoredEntries;
96  		_keyType = keyType;
97  		_valueType = valueType;
98  		_valueTypeIsStatic = valueTypeIsStatic;
99  		_valueTypeSerializer = vts;
100 		_keyTypeSerializer = kts;
101 		_keySerializer = (JsonSerializer<Object>) keySerializer;
102 		_valueSerializer = (JsonSerializer<Object>) valueSerializer;
103 		_dynamicValueSerializers = PropertySerializerMap.emptyMap();
104 		_property = null;
105 	}
106 
107 	@SuppressWarnings("unchecked")
108 	protected KeyValueMapSerializer(KeyValueMapSerializer src,
109 			BeanProperty property, JsonSerializer<?> keySerializer,
110 			JsonSerializer<?> valueSerializer, HashSet<String> ignored) {
111 		super(Map.class, false);
112 		_ignoredEntries = ignored;
113 		_keyType = src._keyType;
114 		_valueType = src._valueType;
115 		_valueTypeIsStatic = src._valueTypeIsStatic;
116 		_valueTypeSerializer = src._valueTypeSerializer;
117 		_keySerializer = (JsonSerializer<Object>) keySerializer;
118 		_keyTypeSerializer = src._keyTypeSerializer;
119 		_valueSerializer = (JsonSerializer<Object>) valueSerializer;
120 		_dynamicValueSerializers = src._dynamicValueSerializers;
121 		_property = property;
122 	}
123 
124 	protected KeyValueMapSerializer(KeyValueMapSerializer src,
125 			TypeSerializer vts) {
126 		super(Map.class, false);
127 		_ignoredEntries = src._ignoredEntries;
128 		_keyType = src._keyType;
129 		_valueType = src._valueType;
130 		_valueTypeIsStatic = src._valueTypeIsStatic;
131 		_valueTypeSerializer = vts;
132 		_keySerializer = src._keySerializer;
133 		_keyTypeSerializer = src._keyTypeSerializer;
134 		_valueSerializer = src._valueSerializer;
135 		_dynamicValueSerializers = src._dynamicValueSerializers;
136 		_property = src._property;
137 	}
138 
139 	@Override
140 	public KeyValueMapSerializer _withValueTypeSerializer(TypeSerializer vts) {
141 		return new KeyValueMapSerializer(this, vts);
142 	}
143 
144 	public KeyValueMapSerializer withResolved(BeanProperty property,
145 			JsonSerializer<?> keySerializer, JsonSerializer<?> valueSerializer,
146 			HashSet<String> ignored) {
147 		return new KeyValueMapSerializer(this, property, keySerializer,
148 				valueSerializer, ignored);
149 	}
150 
151 	/*
152 	 * public static KeyValueMapSerializer construct(String[] ignoredList,
153 	 * JavaType mapType, boolean staticValueType, TypeSerializer vts,
154 	 * JsonSerializer<Object> keySerializer, JsonSerializer<Object>
155 	 * valueSerializer) { HashSet<String> ignoredEntries = toSet(ignoredList);
156 	 * JavaType keyType, valueType;
157 	 * 
158 	 * if (mapType == null) { keyType = valueType = UNSPECIFIED_TYPE; } else {
159 	 * keyType = mapType.getKeyType(); valueType = mapType.getContentType(); }
160 	 * // If value type is final, it's same as forcing static value typing: if
161 	 * (!staticValueType) { staticValueType = (valueType != null &&
162 	 * valueType.isFinal()); } return new KeyValueMapSerializer(ignoredEntries,
163 	 * keyType, valueType, staticValueType, vts, keySerializer,
164 	 * valueSerializer); }
165 	 * 
166 	 * 
167 	 * private static HashSet<String> toSet(String[] ignoredEntries) { if
168 	 * (ignoredEntries == null || ignoredEntries.length == 0) { return null; }
169 	 * HashSet<String> result = new HashSet<String>(ignoredEntries.length); for
170 	 * (String prop : ignoredEntries) { result.add(prop); } return result; }
171 	 */
172 
173 	/*
174 	 * /********************************************************** /*
175 	 * Post-processing (contextualization)
176 	 * /**********************************************************
177 	 */
178 
179 	// @Override
180 	public JsonSerializer<?> createContextual(SerializerProvider provider,
181 			BeanProperty property) throws JsonMappingException {
182 		/*
183 		 * 29-Sep-2012, tatu: Actually, we need to do much more contextual
184 		 * checking here since we finally know for sure the property, and it may
185 		 * have overrides
186 		 */
187 		JsonSerializer<?> ser = null;
188 		JsonSerializer<?> keySer = null;
189 
190 		// First: if we have a property, may have property-annotation overrides
191 		if (property != null) {
192 			AnnotatedMember m = property.getMember();
193 			if (m != null) {
194 				Object serDef;
195 				final AnnotationIntrospector intr = provider
196 						.getAnnotationIntrospector();
197 				serDef = intr.findKeySerializer(m);
198 				if (serDef != null) {
199 					keySer = provider.serializerInstance(m, serDef);
200 				}
201 				serDef = intr.findContentSerializer(m);
202 				if (serDef != null) {
203 					ser = provider.serializerInstance(m, serDef);
204 				}
205 			}
206 		}
207 		if (ser == null) {
208 			ser = _valueSerializer;
209 		}
210 		if (ser == null) {
211 			// 30-Sep-2012, tatu: One more thing -- if explicit content type is
212 			// annotated,
213 			// we can consider it a static case as well.
214 			if (_valueTypeIsStatic
215 					|| hasContentTypeAnnotation(provider, property)) {
216 				ser = provider.findValueSerializer(_valueType, property);
217 			}
218 		} else if (ser instanceof ContextualSerializer) {
219 			ser = ((ContextualSerializer) ser).createContextual(provider,
220 					property);
221 		}
222 		if (keySer == null) {
223 			keySer = _keySerializer;
224 		}
225 		if (keySer == null) {
226 			keySer = provider.findValueSerializer(_keyType, property);
227 		} else if (keySer instanceof ContextualSerializer) {
228 			keySer = ((ContextualSerializer) keySer).createContextual(provider,
229 					property);
230 		}
231 		HashSet<String> ignored = this._ignoredEntries;
232 		AnnotationIntrospector intr = provider.getAnnotationIntrospector();
233 		if (intr != null && property != null) {
234 			String[] moreToIgnore = intr.findPropertiesToIgnore(property
235 					.getMember());
236 			if (moreToIgnore != null) {
237 				ignored = (ignored == null) ? new HashSet<String>()
238 						: new HashSet<String>(ignored);
239 				for (String str : moreToIgnore) {
240 					ignored.add(str);
241 				}
242 			}
243 		}
244 		return withResolved(property, keySer, ser, ignored);
245 	}
246 
247 	/*
248 	 * /********************************************************** /* Accessors
249 	 * /**********************************************************
250 	 */
251 
252 	@Override
253 	public JavaType getContentType() {
254 		return _valueType;
255 	}
256 
257 	@Override
258 	public JsonSerializer<?> getContentSerializer() {
259 		return _valueSerializer;
260 	}
261 
262 	@Override
263 	public boolean isEmpty(Map<?, ?> value) {
264 		return (value == null) || value.isEmpty();
265 	}
266 
267 	@Override
268 	public boolean hasSingleElement(Map<?, ?> value) {
269 		return (value.size() == 1);
270 	}
271 
272 	/*
273 	 * /********************************************************** /* Extended
274 	 * API /**********************************************************
275 	 */
276 
277 	/**
278 	 * Accessor for currently assigned key serializer. Note that this may return
279 	 * null during construction of <code>MapSerializer</code>: depedencies are
280 	 * resolved during {@link #createContextual} method (which can be overridden
281 	 * by custom implementations), but for some dynamic types, it is possible
282 	 * that serializer is only resolved during actual serialization.
283 	 * 
284 	 * @since 2.0
285 	 */
286 	public JsonSerializer<?> getKeySerializer() {
287 		return _keySerializer;
288 	}
289 
290 	/*
291 	 * /********************************************************** /*
292 	 * JsonSerializer implementation
293 	 * /**********************************************************
294 	 */
295 
296 	@Override
297 	public void serialize(Map<?, ?> value, JsonGenerator jgen,
298 			SerializerProvider provider) throws IOException,
299 			JsonGenerationException {
300 		jgen.writeStartArray();
301 		if (!value.isEmpty()) {
302 			if (provider
303 					.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
304 				value = _orderEntries(value);
305 			}
306 			if (_valueSerializer != null) {
307 				serializeFieldsUsing(value, jgen, provider, _valueSerializer);
308 			} else {
309 				serializeFields(value, jgen, provider);
310 			}
311 		}
312 		jgen.writeEndArray();
313 	}
314 
315 	@Override
316 	public void serializeWithType(Map<?, ?> value, JsonGenerator jgen,
317 			SerializerProvider provider, TypeSerializer typeSer)
318 			throws IOException, JsonGenerationException {
319 		typeSer.writeTypePrefixForObject(value, jgen);
320 		jgen.writeStartArray();
321 		if (!value.isEmpty()) {
322 			if (provider
323 					.isEnabled(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS)) {
324 				value = _orderEntries(value);
325 			}
326 			if (_valueSerializer != null) {
327 				serializeFieldsUsing(value, jgen, provider, _valueSerializer);
328 			} else {
329 				serializeFields(value, jgen, provider);
330 			}
331 		}
332 		jgen.writeEndArray();
333 		typeSer.writeTypeSuffixForObject(value, jgen);
334 		
335 	}
336 
337 	/*
338 	 * /********************************************************** /*
339 	 * JsonSerializer implementation
340 	 * /**********************************************************
341 	 */
342 
343 	/**
344 	 * Method called to serialize fields, when the value type is not statically
345 	 * known.
346 	 */
347 	public void serializeFields(Map<?, ?> value, JsonGenerator jgen,
348 			SerializerProvider provider) throws IOException,
349 			JsonGenerationException {
350 		// If value type needs polymorphic type handling, some more work needed:
351 		if (_valueTypeSerializer != null || _keyTypeSerializer != null) {
352 			serializeTypedFields(value, jgen, provider);
353 			return;
354 		}
355 		final JsonSerializer<Object> keySerializer = _keySerializer;
356 
357 		final HashSet<String> ignored = _ignoredEntries;
358 		final boolean skipNulls = !provider
359 				.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
360 
361 		PropertySerializerMap serializers = _dynamicValueSerializers;
362 
363 		for (Map.Entry<?, ?> entry : value.entrySet()) {
364 			Object valueElem = entry.getValue();
365 			// First, serialize key
366 			Object keyElem = entry.getKey();
367 			
368 			jgen.writeStartObject();
369 			jgen.writeFieldName(MAP_KEY_NAME);
370 			
371 			if (keyElem == null) {
372 				provider.findNullKeySerializer(_keyType, _property).serialize(
373 						null, jgen, provider);
374 			} else {
375 				// [JACKSON-314] skip entries with null values?
376 				if (skipNulls && valueElem == null)
377 					continue;
378 				// One twist: is entry ignorable? If so, skip
379 				if (ignored != null && ignored.contains(keyElem))
380 					continue;
381 
382 				try {
383 					keySerializer.serialize(keyElem, jgen, provider);
384 				} catch (Exception ex) {
385 					Class<?> cc = keyElem.getClass();
386 					JsonSerializer<Object> serializer = serializers
387 							.serializerFor(cc);
388 					if (serializer == null) {
389 						if (_keyType.hasGenericTypes()) {
390 							serializer = _findAndAddDynamic(serializers,
391 									provider.constructSpecializedType(_keyType,
392 											cc), provider);
393 						} else {
394 							serializer = _findAndAddDynamic(serializers, cc,
395 									provider);
396 						}
397 						serializers = _dynamicValueSerializers;
398 					}
399 					try {
400 						serializer.serialize(keyElem, jgen, provider);
401 					} catch (Exception e) {
402 						// [JACKSON-55] Need to add reference information
403 						String keyDesc = "" + keyElem;
404 						wrapAndThrow(provider, e, value, keyDesc);
405 					}
406 				}
407 			}
408 
409 			jgen.writeFieldName(MAP_VALUE_NAME);
410 			
411 			// And then value
412 			if (valueElem == null) {
413 				provider.defaultSerializeNull(jgen);
414 			} else {
415 				Class<?> cc = valueElem.getClass();
416 				JsonSerializer<Object> serializer = serializers
417 						.serializerFor(cc);
418 				if (serializer == null) {
419 					if (_valueType.hasGenericTypes()) {
420 						serializer = _findAndAddDynamic(serializers,
421 								provider.constructSpecializedType(_valueType,
422 										cc), provider);
423 					} else {
424 						serializer = _findAndAddDynamic(serializers, cc,
425 								provider);
426 					}
427 					serializers = _dynamicValueSerializers;
428 				}
429 				try {
430 					serializer.serialize(valueElem, jgen, provider);
431 				} catch (Exception e) {
432 					// [JACKSON-55] Need to add reference information
433 					String keyDesc = "" + keyElem;
434 					wrapAndThrow(provider, e, value, keyDesc);
435 				}
436 			}
437 			
438 			jgen.writeEndObject();
439 		}
440 	}
441 
442 	/**
443 	 * Method called to serialize fields, when the value type is statically
444 	 * known, so that value serializer is passed and does not need to be fetched
445 	 * from provider.
446 	 */
447 	protected void serializeFieldsUsing(Map<?, ?> value, JsonGenerator jgen,
448 			SerializerProvider provider, JsonSerializer<Object> ser)
449 			throws IOException, JsonGenerationException {
450 		final JsonSerializer<Object> keySerializer = _keySerializer;
451 		final HashSet<String> ignored = _ignoredEntries;
452 		final TypeSerializer keySer = _keyTypeSerializer;
453 		final TypeSerializer typeSer = _valueTypeSerializer;
454 		final boolean skipNulls = !provider
455 				.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
456 
457 		for (Map.Entry<?, ?> entry : value.entrySet()) {
458 			Object valueElem = entry.getValue();
459 			Object keyElem = entry.getKey();
460 
461 			jgen.writeStartObject();
462 
463 			jgen.writeFieldName(MAP_KEY_NAME);
464 
465 			if (keyElem == null) {
466 				provider.findNullKeySerializer(_keyType, _property).serialize(
467 						null, jgen, provider);
468 			} else {
469 				// [JACKSON-314] skip entries with null values?
470 				if (skipNulls && valueElem == null)
471 					continue;
472 				// One twist: is entry ignorable? If so, skip
473 				if (ignored != null && ignored.contains(keyElem))
474 					continue;
475 
476 				try {
477 					if (keySer == null) {
478 						keySerializer.serialize(keyElem, jgen, provider);
479 					} else {
480 						keySerializer.serializeWithType(keyElem, jgen,
481 								provider, keySer);
482 					}
483 				} catch (Exception e) {
484 					// [JACKSON-55] Need to add reference information
485 					String keyDesc = "" + keyElem;
486 					wrapAndThrow(provider, e, value, keyDesc);
487 				}
488 			}
489 
490 			jgen.writeFieldName(MAP_VALUE_NAME);
491 
492 			if (valueElem == null) {
493 				provider.defaultSerializeNull(jgen);
494 			} else {
495 				try {
496 					if (typeSer == null) {
497 						ser.serialize(valueElem, jgen, provider);
498 					} else {
499 						ser.serializeWithType(valueElem, jgen, provider,
500 								typeSer);
501 					}
502 				} catch (Exception e) {
503 					// [JACKSON-55] Need to add reference information
504 					String keyDesc = "" + keyElem;
505 					wrapAndThrow(provider, e, value, keyDesc);
506 				}
507 			}
508 
509 			jgen.writeEndObject();
510 		}
511 	}
512 
513 	protected void serializeTypedFields(Map<?, ?> value, JsonGenerator jgen,
514 			SerializerProvider provider) throws IOException,
515 			JsonGenerationException {
516 		final JsonSerializer<Object> keySerializer = _keySerializer;
517 		JsonSerializer<Object> prevValueSerializer = null;
518 		Class<?> prevValueClass = null;
519 		final HashSet<String> ignored = _ignoredEntries;
520 		final boolean skipNulls = !provider
521 				.isEnabled(SerializationFeature.WRITE_NULL_MAP_VALUES);
522 
523 		for (Map.Entry<?, ?> entry : value.entrySet()) {
524 			Object valueElem = entry.getValue();
525 			// First, serialize key
526 			Object keyElem = entry.getKey();
527 			
528 			jgen.writeStartObject();
529 			jgen.writeFieldName(MAP_KEY_NAME);
530 			
531 			
532 			if (keyElem == null) {
533 				provider.findNullKeySerializer(_keyType, _property).serialize(
534 						null, jgen, provider);
535 			} else {
536 				// TODO: check that case (probably will be [{key}]
537 				// need to move skipping checks above
538 				
539 				// [JACKSON-314] also may need to skip entries with null values
540 				if (skipNulls && valueElem == null)
541 					continue;
542 				// One twist: is entry ignorable? If so, skip
543 				if (ignored != null && ignored.contains(keyElem))
544 					continue;
545 				keySerializer.serialize(keyElem, jgen, provider);
546 			}
547 
548 			jgen.writeFieldName(MAP_VALUE_NAME);
549 			
550 			// And then value
551 			if (valueElem == null) {
552 				provider.defaultSerializeNull(jgen);
553 			} else {
554 				Class<?> cc = valueElem.getClass();
555 				JsonSerializer<Object> currSerializer;
556 				if (cc == prevValueClass) {
557 					currSerializer = prevValueSerializer;
558 				} else {
559 					currSerializer = provider
560 							.findValueSerializer(cc, _property);
561 					prevValueSerializer = currSerializer;
562 					prevValueClass = cc;
563 				}
564 				try {
565 					currSerializer.serializeWithType(valueElem, jgen, provider,
566 							_valueTypeSerializer);
567 				} catch (Exception e) {
568 					// [JACKSON-55] Need to add reference information
569 					String keyDesc = "" + keyElem;
570 					wrapAndThrow(provider, e, value, keyDesc);
571 				}
572 			}
573 			
574 			jgen.writeEndObject();
575 		}
576 	}
577 
578 	@Override
579 	public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
580 		ObjectNode o = createSchemaNode("object", true);
581 		// (ryan) even though it's possible to statically determine the "value"
582 		// type of the map,
583 		// there's no way to statically determine the keys, so the "Entries"
584 		// can't be determined.
585 		return o;
586 	}
587 
588 	@Override
589 	public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor,
590 			JavaType typeHint) throws JsonMappingException {
591 		// jackphel Aug 17 2012 : this can definitely more more exact.
592 		visitor.expectObjectFormat(typeHint);
593 	}
594 
595 	/*
596 	 * /********************************************************** /* Internal
597 	 * helper methods
598 	 * /**********************************************************
599 	 */
600 	@SuppressWarnings("deprecation")
601 	protected final JsonSerializer<Object> _findAndAddDynamic(
602 			PropertySerializerMap map, Class<?> type,
603 			SerializerProvider provider) throws JsonMappingException {
604 		PropertySerializerMap.SerializerAndMapResult result = map
605 				.findAndAddSerializer(type, provider, _property);
606 		// did we get a new map of serializers? If so, start using it
607 		if (map != result.map) {
608 			_dynamicValueSerializers = result.map;
609 		}
610 		return result.serializer;
611 	}
612 	
613 	@SuppressWarnings("deprecation")
614 	protected final JsonSerializer<Object> _findAndAddDynamic(
615 			PropertySerializerMap map, JavaType type,
616 			SerializerProvider provider) throws JsonMappingException {
617 		PropertySerializerMap.SerializerAndMapResult result = map
618 				.findAndAddSerializer(type, provider, _property);
619 		if (map != result.map) {
620 			_dynamicValueSerializers = result.map;
621 		}
622 		return result.serializer;
623 	}
624 
625 	protected Map<?, ?> _orderEntries(Map<?, ?> input) {
626 		// minor optimization: may already be sorted?
627 		if (input instanceof SortedMap<?, ?>) {
628 			return input;
629 		}
630 		return new TreeMap<Object, Object>(input);
631 	}
632 
633 }