package com.fasterxml.jackson.databind.deser.std;

import java.io.IOException;
import java.util.*;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.deser.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;

Basic serializer that can take JSON "Object" structure and construct a Map instance, with typed contents.

Note: for untyped content (one indicated by passing Object.class as the type), UntypedObjectDeserializer is used instead. It can also construct Maps, but not with specific POJO types, only other containers and primitives/wrappers.

/** * Basic serializer that can take JSON "Object" structure and * construct a {@link java.util.Map} instance, with typed contents. *<p> * Note: for untyped content (one indicated by passing Object.class * as the type), {@link UntypedObjectDeserializer} is used instead. * It can also construct {@link java.util.Map}s, but not with specific * POJO types, only other containers and primitives/wrappers. */
@JacksonStdImpl public class MapEntryDeserializer extends ContainerDeserializerBase<Map.Entry<Object,Object>> implements ContextualDeserializer { private static final long serialVersionUID = 1; // // Configuration: typing, deserializers
Key deserializer to use; either passed via constructor (when indicated by annotations), or resolved when createContextual is called;
/** * Key deserializer to use; either passed via constructor * (when indicated by annotations), or resolved when * {@link #createContextual} is called; */
protected final KeyDeserializer _keyDeserializer;
Value deserializer.
/** * Value deserializer. */
protected final JsonDeserializer<Object> _valueDeserializer;
If value instances have polymorphic type information, this is the type deserializer that can handle it
/** * If value instances have polymorphic type information, this * is the type deserializer that can handle it */
protected final TypeDeserializer _valueTypeDeserializer; /* /********************************************************** /* Life-cycle /********************************************************** */ public MapEntryDeserializer(JavaType type, KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser) { super(type); if (type.containedTypeCount() != 2) { // sanity check throw new IllegalArgumentException("Missing generic type information for "+type); } _keyDeserializer = keyDeser; _valueDeserializer = valueDeser; _valueTypeDeserializer = valueTypeDeser; }
Copy-constructor that can be used by sub-classes to allow copy-on-write styling copying of settings of an existing instance.
/** * Copy-constructor that can be used by sub-classes to allow * copy-on-write styling copying of settings of an existing instance. */
protected MapEntryDeserializer(MapEntryDeserializer src) { super(src); _keyDeserializer = src._keyDeserializer; _valueDeserializer = src._valueDeserializer; _valueTypeDeserializer = src._valueTypeDeserializer; } protected MapEntryDeserializer(MapEntryDeserializer src, KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser, TypeDeserializer valueTypeDeser) { super(src); _keyDeserializer = keyDeser; _valueDeserializer = valueDeser; _valueTypeDeserializer = valueTypeDeser; }
Fluent factory method used to create a copy with slightly different settings. When sub-classing, MUST be overridden.
/** * Fluent factory method used to create a copy with slightly * different settings. When sub-classing, MUST be overridden. */
@SuppressWarnings("unchecked") protected MapEntryDeserializer withResolved(KeyDeserializer keyDeser, TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser) { if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser) && (_valueTypeDeserializer == valueTypeDeser)) { return this; } return new MapEntryDeserializer(this, keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser); } /* /********************************************************** /* Validation, post-processing (ResolvableDeserializer) /********************************************************** */
Method called to finalize setup of this deserializer, when it is known for which property deserializer is needed for.
/** * Method called to finalize setup of this deserializer, * when it is known for which property deserializer is needed for. */
@Override public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException { KeyDeserializer kd = _keyDeserializer; if (kd == null) { kd = ctxt.findKeyDeserializer(_containerType.containedType(0), property); } else { if (kd instanceof ContextualKeyDeserializer) { kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property); } } JsonDeserializer<?> vd = _valueDeserializer; vd = findConvertingContentDeserializer(ctxt, property, vd); JavaType contentType = _containerType.containedType(1); if (vd == null) { vd = ctxt.findContextualValueDeserializer(contentType, property); } else { // if directly assigned, probably not yet contextual, so: vd = ctxt.handleSecondaryContextualization(vd, property, contentType); } TypeDeserializer vtd = _valueTypeDeserializer; if (vtd != null) { vtd = vtd.forProperty(property); } return withResolved(kd, vtd, vd); } /* /********************************************************** /* ContainerDeserializerBase API /********************************************************** */ @Override public JavaType getContentType() { return _containerType.containedType(1); } @Override public JsonDeserializer<Object> getContentDeserializer() { return _valueDeserializer; } /* /********************************************************** /* JsonDeserializer API /********************************************************** */ @SuppressWarnings("unchecked") @Override public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT JsonToken t = p.currentToken(); if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) { // String may be ok however: // slightly redundant (since String was passed above), but return _deserializeFromEmpty(p, ctxt); } if (t == JsonToken.START_OBJECT) { t = p.nextToken(); } if (t != JsonToken.FIELD_NAME) { if (t == JsonToken.END_OBJECT) { return ctxt.reportInputMismatch(this, "Cannot deserialize a Map.Entry out of empty JSON Object"); } return (Map.Entry<Object,Object>) ctxt.handleUnexpectedToken(handledType(), p); } final KeyDeserializer keyDes = _keyDeserializer; final JsonDeserializer<Object> valueDes = _valueDeserializer; final TypeDeserializer typeDeser = _valueTypeDeserializer; final String keyStr = p.getCurrentName(); Object key = keyDes.deserializeKey(keyStr, ctxt); Object value = null; // And then the value... t = p.nextToken(); try { // Note: must handle null explicitly here; value deserializers won't if (t == JsonToken.VALUE_NULL) { value = valueDes.getNullValue(ctxt); } else if (typeDeser == null) { value = valueDes.deserialize(p, ctxt); } else { value = valueDes.deserializeWithType(p, ctxt, typeDeser); } } catch (Exception e) { wrapAndThrow(e, Map.Entry.class, keyStr); } // Close, but also verify that we reached the END_OBJECT t = p.nextToken(); if (t != JsonToken.END_OBJECT) { if (t == JsonToken.FIELD_NAME) { // most likely ctxt.reportInputMismatch(this, "Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '%s')", p.getCurrentName()); } else { // how would this occur? ctxt.reportInputMismatch(this, "Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t); } return null; } return new AbstractMap.SimpleEntry<Object,Object>(key, value); } @Override public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt, Map.Entry<Object,Object> result) throws IOException { throw new IllegalStateException("Cannot update Map.Entry values"); } @Override public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { // In future could check current token... for now this should be enough: return typeDeserializer.deserializeTypedFromObject(p, ctxt); } }