1 package top.infra.jackson2.deser;
2
3 import com.fasterxml.jackson.core.JsonParser;
4 import com.fasterxml.jackson.core.JsonProcessingException;
5 import com.fasterxml.jackson.core.JsonToken;
6 import com.fasterxml.jackson.databind.BeanProperty;
7 import com.fasterxml.jackson.databind.DeserializationContext;
8 import com.fasterxml.jackson.databind.JavaType;
9 import com.fasterxml.jackson.databind.JsonDeserializer;
10 import com.fasterxml.jackson.databind.JsonMappingException;
11 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
12 import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
13 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
14
15 import org.apache.commons.lang3.tuple.ImmutablePair;
16 import org.apache.commons.lang3.tuple.Pair;
17
18 import java.io.IOException;
19 import java.lang.reflect.InvocationTargetException;
20
21
22
23
24 public class PairDeserializer extends StdDeserializer<Pair<Object, Object>> implements ContextualDeserializer {
25
26 private static final long serialVersionUID = 1;
27
28
29
30 protected final JavaType _type;
31
32
33
34
35
36
37 protected final JsonDeserializer<Object> _keyDeserializer;
38
39
40
41
42 protected final TypeDeserializer _keyTypeDeserializer;
43
44
45
46
47 protected final JsonDeserializer<Object> _valueDeserializer;
48
49
50
51
52
53 protected final TypeDeserializer _valueTypeDeserializer;
54
55
56
57
58
59
60
61 public PairDeserializer(
62 final JavaType type,
63 final JsonDeserializer<Object> keyDeser,
64 final TypeDeserializer keyTypeDeserializer,
65 final JsonDeserializer<Object> valueDeser,
66 final TypeDeserializer valueTypeDeser
67 ) {
68 super(type);
69 if (type.containedTypeCount() != 2) {
70 throw new IllegalArgumentException("Missing generic type information for " + type);
71 }
72 this._type = type;
73 this._keyDeserializer = keyDeser;
74 this._keyTypeDeserializer = keyTypeDeserializer;
75 this._valueDeserializer = valueDeser;
76 this._valueTypeDeserializer = valueTypeDeser;
77 }
78
79
80
81
82
83 protected PairDeserializer(final PairDeserializer src) {
84 super(src._type);
85 this._type = src._type;
86 this._keyDeserializer = src._keyDeserializer;
87 this._keyTypeDeserializer = src._keyTypeDeserializer;
88 this._valueDeserializer = src._valueDeserializer;
89 this._valueTypeDeserializer = src._valueTypeDeserializer;
90 }
91
92 protected PairDeserializer(
93 final PairDeserializer src,
94 final JsonDeserializer<Object> keyDeser,
95 final TypeDeserializer keyTypeDeserializer,
96 final JsonDeserializer<Object> valueDeser,
97 final TypeDeserializer valueTypeDeser
98 ) {
99 super(src._type);
100 this._type = src._type;
101 this._keyDeserializer = keyDeser;
102 this._keyTypeDeserializer = keyTypeDeserializer;
103 this._valueDeserializer = valueDeser;
104 this._valueTypeDeserializer = valueTypeDeser;
105 }
106
107
108
109
110
111 @SuppressWarnings("unchecked")
112 protected PairDeserializer withResolved(
113 final TypeDeserializer keyTypeDeserializer, final JsonDeserializer<?> keyDeser,
114 final TypeDeserializer valueTypeDeser, final JsonDeserializer<?> valueDeser
115 ) {
116 if ((this._keyDeserializer == keyDeser) && (this._valueDeserializer == valueDeser)
117 && (this._valueTypeDeserializer == valueTypeDeser)) {
118 return this;
119 }
120 return new PairDeserializer(
121 this,
122 (JsonDeserializer<Object>) keyDeser,
123 keyTypeDeserializer,
124 (JsonDeserializer<Object>) valueDeser,
125 valueTypeDeser);
126 }
127
128
129
130
131
132
133
134
135
136
137
138 @Override
139 public JsonDeserializer<?> createContextual(
140 final DeserializationContext ctxt,
141 final BeanProperty property
142 ) throws JsonMappingException {
143 JsonDeserializer<?> kd = this._keyDeserializer;
144 kd = findConvertingContentDeserializer(ctxt, property, kd);
145 JavaType keyType = this._type.containedType(0);
146 if (kd == null) {
147 kd = ctxt.findContextualValueDeserializer(keyType, property);
148 } else {
149 kd = ctxt.handleSecondaryContextualization(kd, property, keyType);
150 }
151 TypeDeserializer ktd = this._keyTypeDeserializer;
152 if (ktd != null) {
153 ktd = ktd.forProperty(property);
154 }
155
156 JsonDeserializer<?> vd = this._valueDeserializer;
157 vd = findConvertingContentDeserializer(ctxt, property, vd);
158 JavaType valueType = this._type.containedType(1);
159 if (vd == null) {
160 vd = ctxt.findContextualValueDeserializer(valueType, property);
161 } else {
162 vd = ctxt.handleSecondaryContextualization(vd, property, valueType);
163 }
164 TypeDeserializer vtd = this._valueTypeDeserializer;
165 if (vtd != null) {
166 vtd = vtd.forProperty(property);
167 }
168 return withResolved(ktd, kd, vtd, vd);
169 }
170
171
172
173
174
175
176
177 @SuppressWarnings("unchecked")
178 @Override
179 public Pair<Object, Object> deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException {
180
181 JsonToken t = p.getCurrentToken();
182 if (t != JsonToken.START_OBJECT && t != JsonToken.FIELD_NAME && t != JsonToken.END_OBJECT) {
183
184
185 return _deserializeFromEmpty(p, ctxt);
186 }
187 if (t == JsonToken.START_OBJECT) {
188 t = p.nextToken();
189 }
190 if (t != JsonToken.FIELD_NAME) {
191 if (t == JsonToken.END_OBJECT) {
192 ctxt.reportMappingException("Can not deserialize a Pair out of empty JSON Object");
193 return null;
194 }
195 return (Pair<Object, Object>) ctxt.handleUnexpectedToken(handledType(), p);
196 }
197
198 Object key = null;
199 Object value = null;
200 Pair<String, Object> field = this.deserializeField(p, ctxt);
201 if ("left".equals(field.getKey())) {
202 key = field.getRight();
203 } else {
204 value = field.getRight();
205 }
206 t = p.nextToken();
207 field = this.deserializeField(p, ctxt);
208 if ("left".equals(field.getKey())) {
209 key = field.getRight();
210 } else {
211 value = field.getRight();
212 }
213
214
215 t = p.nextToken();
216 if (t != JsonToken.END_OBJECT) {
217 if (t == JsonToken.FIELD_NAME) {
218 ctxt.reportMappingException("Problem binding JSON into Pair: more than one entry in JSON (second field: '" + p.getCurrentName() + "')");
219 } else {
220
221 ctxt.reportMappingException("Problem binding JSON into Pair: unexpected content after JSON Object entry: " + t);
222 }
223 return null;
224 }
225 return new ImmutablePair<>(key, value);
226 }
227
228 private Pair<String, Object> deserializeField(
229 final JsonParser p,
230 final DeserializationContext ctxt
231 ) throws IOException {
232 final String fieldName = p.getCurrentName();
233 Object fieldValue = null;
234 if ("left".equals(fieldName)) {
235 final JsonDeserializer<Object> keyDes = this._keyDeserializer;
236 final TypeDeserializer keyTypeDeser = this._keyTypeDeserializer;
237 fieldValue = this.deserializeField(p, ctxt, fieldName, keyDes, keyTypeDeser);
238 } else if ("right".equals(fieldName)) {
239 final JsonDeserializer<Object> valueDes = this._valueDeserializer;
240 final TypeDeserializer valueTypeDeser = this._valueTypeDeserializer;
241 fieldValue = this.deserializeField(p, ctxt, fieldName, valueDes, valueTypeDeser);
242 } else {
243 ctxt.reportMappingException("Problem binding JSON into Pair: unknown field " + fieldName + ".");
244 }
245 return new ImmutablePair<>(fieldName, fieldValue);
246 }
247
248 private Object deserializeField(
249 final JsonParser p,
250 final DeserializationContext ctxt,
251 final String fieldName,
252 final JsonDeserializer<Object> des,
253 final TypeDeserializer typeDeser
254 ) throws IOException {
255 Object fieldValue = null;
256 JsonToken t = p.nextToken();
257 try {
258
259 if (t == JsonToken.VALUE_NULL) {
260 fieldValue = des.getNullValue(ctxt);
261 } else if (typeDeser == null) {
262 fieldValue = des.deserialize(p, ctxt);
263 } else {
264 fieldValue = des.deserializeWithType(p, ctxt, typeDeser);
265 }
266 } catch (final Exception ex) {
267 wrapAndThrow(ex, Pair.class, fieldName);
268 }
269 return fieldValue;
270 }
271
272 @Override
273 public Pair<Object, Object> deserialize(
274 final JsonParser p, final DeserializationContext ctxt, final Pair<Object, Object> result
275 ) throws IOException {
276 throw new IllegalStateException("Can not update Pair values");
277 }
278
279 @Override
280 public Object deserializeWithType(
281 final JsonParser p,
282 final DeserializationContext ctxt,
283 final TypeDeserializer typeDeserializer
284 ) throws IOException, JsonProcessingException {
285
286 return typeDeserializer.deserializeTypedFromObject(p, ctxt);
287 }
288
289
290
291
292
293
294
295 @Override
296 public JavaType getValueType() {
297 return this._type;
298 }
299
300
301
302
303
304
305
306
307
308
309 protected void wrapAndThrow(Throwable t, final Object ref, String key) throws IOException {
310
311 while (t instanceof InvocationTargetException && t.getCause() != null) {
312 t = t.getCause();
313 }
314
315 if (t instanceof Error) {
316 throw (Error) t;
317 }
318
319 if (t instanceof IOException && !(t instanceof JsonMappingException)) {
320 throw (IOException) t;
321 }
322
323 if (key == null) {
324 key = "N/A";
325 }
326 throw JsonMappingException.wrapWithPath(t, ref, key);
327 }
328 }