View Javadoc
1   package edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.databind.deser.std;
2   
3   import java.io.IOException;
4   import java.util.*;
5   
6   import edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.core.*;
7   import edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.databind.*;
8   import edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
9   import edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.databind.deser.*;
10  import edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
11  import edu.internet2.middleware.grouperClientExt.com.fasterxml.jackson.databind.type.LogicalType;
12  
13  /**
14   * Basic serializer that can take JSON "Object" structure and
15   * construct a {@link java.util.Map} instance, with typed contents.
16   *<p>
17   * Note: for untyped content (one indicated by passing Object.class
18   * as the type), {@link UntypedObjectDeserializer} is used instead.
19   * It can also construct {@link java.util.Map}s, but not with specific
20   * POJO types, only other containers and primitives/wrappers.
21   */
22  @JacksonStdImpl
23  public class MapEntryDeserializer
24      extends ContainerDeserializerBase<Map.Entry<Object,Object>>
25      implements ContextualDeserializer
26  {
27      private static final long serialVersionUID = 1;
28  
29      // // Configuration: typing, deserializers
30  
31      /**
32       * Key deserializer to use; either passed via constructor
33       * (when indicated by annotations), or resolved when
34       * {@link #createContextual} is called;
35       */
36      protected final KeyDeserializer _keyDeserializer;
37  
38      /**
39       * Value deserializer.
40       */
41      protected final JsonDeserializer<Object> _valueDeserializer;
42  
43      /**
44       * If value instances have polymorphic type information, this
45       * is the type deserializer that can handle it
46       */
47      protected final TypeDeserializer _valueTypeDeserializer;
48  
49      /*
50      /**********************************************************
51      /* Life-cycle
52      /**********************************************************
53       */
54  
55      public MapEntryDeserializer(JavaType type,
56              KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
57              TypeDeserializer valueTypeDeser)
58      {
59          super(type);
60          if (type.containedTypeCount() != 2) { // sanity check
61              throw new IllegalArgumentException("Missing generic type information for "+type);
62          }
63          _keyDeserializer = keyDeser;
64          _valueDeserializer = valueDeser;
65          _valueTypeDeserializer = valueTypeDeser;
66      }
67  
68      /**
69       * Copy-constructor that can be used by sub-classes to allow
70       * copy-on-write styling copying of settings of an existing instance.
71       */
72      protected MapEntryDeserializer./../../../../../../../../edu/internet2/middleware/grouperClientExt/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.html#MapEntryDeserializer">MapEntryDeserializer(MapEntryDeserializer src)
73      {
74          super(src);
75          _keyDeserializer = src._keyDeserializer;
76          _valueDeserializer = src._valueDeserializer;
77          _valueTypeDeserializer = src._valueTypeDeserializer;
78      }
79  
80      protected MapEntryDeserializer./../../../../../../../../edu/internet2/middleware/grouperClientExt/com/fasterxml/jackson/databind/deser/std/MapEntryDeserializer.html#MapEntryDeserializer">MapEntryDeserializer(MapEntryDeserializer src,
81              KeyDeserializer keyDeser, JsonDeserializer<Object> valueDeser,
82              TypeDeserializer valueTypeDeser)
83      {
84          super(src);
85          _keyDeserializer = keyDeser;
86          _valueDeserializer = valueDeser;
87          _valueTypeDeserializer = valueTypeDeser;
88      }
89  
90      /**
91       * Fluent factory method used to create a copy with slightly
92       * different settings. When sub-classing, MUST be overridden.
93       */
94      @SuppressWarnings("unchecked")
95      protected MapEntryDeserializer withResolved(KeyDeserializer keyDeser,
96              TypeDeserializer valueTypeDeser, JsonDeserializer<?> valueDeser)
97      {
98          
99          if ((_keyDeserializer == keyDeser) && (_valueDeserializer == valueDeser)
100                 && (_valueTypeDeserializer == valueTypeDeser)) {
101             return this;
102         }
103         return new MapEntryDeserializer(this,
104                 keyDeser, (JsonDeserializer<Object>) valueDeser, valueTypeDeser);
105     }
106 
107     @Override // since 2.12
108     public LogicalType logicalType() {
109         // Slightly tricky, could consider POJO too?
110         return LogicalType.Map;
111     }
112 
113     /*
114     /**********************************************************
115     /* Validation, post-processing (ResolvableDeserializer)
116     /**********************************************************
117      */
118 
119     /**
120      * Method called to finalize setup of this deserializer,
121      * when it is known for which property deserializer is needed for.
122      */
123     @Override
124     public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
125             BeanProperty property) throws JsonMappingException
126     {
127         KeyDeserializer kd = _keyDeserializer;
128         if (kd == null) {
129             kd = ctxt.findKeyDeserializer(_containerType.containedType(0), property);
130         } else {
131             if (kd instanceof ContextualKeyDeserializer) {
132                 kd = ((ContextualKeyDeserializer) kd).createContextual(ctxt, property);
133             }
134         }
135         JsonDeserializer<?> vd = _valueDeserializer;
136         vd = findConvertingContentDeserializer(ctxt, property, vd);
137         JavaType contentType = _containerType.containedType(1);
138         if (vd == null) {
139             vd = ctxt.findContextualValueDeserializer(contentType, property);
140         } else { // if directly assigned, probably not yet contextual, so:
141             vd = ctxt.handleSecondaryContextualization(vd, property, contentType);
142         }
143         TypeDeserializer vtd = _valueTypeDeserializer;
144         if (vtd != null) {
145             vtd = vtd.forProperty(property);
146         }
147         return withResolved(kd, vtd, vd);
148     }
149 
150     /*
151     /**********************************************************
152     /* ContainerDeserializerBase API
153     /**********************************************************
154      */
155 
156     @Override
157     public JavaType getContentType() {
158         return _containerType.containedType(1);
159     }
160 
161     @Override
162     public JsonDeserializer<Object> getContentDeserializer() {
163         return _valueDeserializer;
164     }
165 
166     // 31-May-2020, tatu: Should probably define but we don't have it yet
167 //    public ValueInstantiator getValueInstantiator() { }
168 
169     /*
170     /**********************************************************
171     /* JsonDeserializer API
172     /**********************************************************
173      */
174 
175     @SuppressWarnings("unchecked")
176     @Override
177     public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
178     {
179         // Ok: must point to START_OBJECT, FIELD_NAME or END_OBJECT
180         JsonToken t = p.currentToken();
181         if (t == JsonToken.START_OBJECT) {
182             t = p.nextToken();
183         } else if (t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
184             // Empty array, or single-value wrapped in array?
185             if (t == JsonToken.START_ARRAY) {
186                 return _deserializeFromArray(p, ctxt);
187             }
188             return (Map.Entry<Object,Object>) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
189         }
190         if (t != JsonToken.FIELD_NAME) {
191             if (t == JsonToken.END_OBJECT) {
192                 return ctxt.reportInputMismatch(this,
193                         "Cannot deserialize a Map.Entry out of empty JSON Object");
194             }
195             return (Map.Entry<Object,Object>) ctxt.handleUnexpectedToken(handledType(), p);
196         }
197 
198         final KeyDeserializer keyDes = _keyDeserializer;
199         final JsonDeserializer<Object> valueDes = _valueDeserializer;
200         final TypeDeserializer typeDeser = _valueTypeDeserializer;
201 
202         final String keyStr = p.currentName();
203         Object key = keyDes.deserializeKey(keyStr, ctxt);
204         Object value = null;
205         // And then the value...
206         t = p.nextToken();
207         try {
208             // Note: must handle null explicitly here; value deserializers won't
209             if (t == JsonToken.VALUE_NULL) {
210                 value = valueDes.getNullValue(ctxt);
211             } else if (typeDeser == null) {
212                 value = valueDes.deserialize(p, ctxt);
213             } else {
214                 value = valueDes.deserializeWithType(p, ctxt, typeDeser);
215             }
216         } catch (Exception e) {
217             wrapAndThrow(ctxt, e, Map.Entry.class, keyStr);
218         }
219 
220         // Close, but also verify that we reached the END_OBJECT
221         t = p.nextToken();
222         if (t != JsonToken.END_OBJECT) {
223             if (t == JsonToken.FIELD_NAME) { // most likely
224                 ctxt.reportInputMismatch(this,
225                         "Problem binding JSON into Map.Entry: more than one entry in JSON (second field: '%s')",
226                         p.currentName());
227             } else {
228                 // how would this occur?
229                 ctxt.reportInputMismatch(this,
230                         "Problem binding JSON into Map.Entry: unexpected content after JSON Object entry: "+t);
231             }
232             return null;
233         }
234         return new AbstractMap.SimpleEntry<Object,Object>(key, value);
235     }
236 
237     @Override
238     public Map.Entry<Object,Object> deserialize(JsonParser p, DeserializationContext ctxt,
239             Map.Entry<Object,Object> result) throws IOException
240     {
241         throw new IllegalStateException("Cannot update Map.Entry values");
242     }
243 
244     @Override
245     public Object deserializeWithType(JsonParser p, DeserializationContext ctxt,
246             TypeDeserializer typeDeserializer)
247         throws IOException
248     {
249         // In future could check current token... for now this should be enough:
250         return typeDeserializer.deserializeTypedFromObject(p, ctxt);
251     }
252 }