1515 */
1616package org .springframework .data .redis .hash ;
1717
18- import static com .fasterxml .jackson .databind .ObjectMapper .DefaultTyping .* ;
18+ import static com .fasterxml .jackson .databind .ObjectMapper .DefaultTyping .EVERYTHING ;
1919
2020import java .io .IOException ;
2121import java .text .ParseException ;
2222import java .util .ArrayList ;
23+ import java .util .Arrays ;
2324import java .util .Calendar ;
25+ import java .util .Collections ;
2426import java .util .Date ;
2527import java .util .HashMap ;
2628import java .util .Iterator ;
3234import java .util .Set ;
3335
3436import org .springframework .data .mapping .MappingException ;
37+ import org .springframework .data .redis .support .collections .CollectionUtils ;
3538import org .springframework .data .util .DirectFieldAccessFallbackBeanWrapper ;
39+ import org .springframework .lang .NonNull ;
40+ import org .springframework .lang .Nullable ;
3641import org .springframework .util .Assert ;
3742import org .springframework .util .ClassUtils ;
3843import org .springframework .util .NumberUtils ;
39- import org .springframework .util .ObjectUtils ;
4044import org .springframework .util .StringUtils ;
4145
4246import com .fasterxml .jackson .annotation .JsonInclude .Include ;
@@ -233,8 +237,12 @@ public Object fromHash(Map<String, Object> hash) {
233237
234238 if (flatten ) {
235239
236- return typingMapper .reader ().forType (Object .class )
237- .readValue (untypedMapper .writeValueAsBytes (doUnflatten (hash )));
240+ Map <String , Object > unflattenedHash = doUnflatten (hash );
241+ byte [] unflattenedHashedBytes = untypedMapper .writeValueAsBytes (unflattenedHash );
242+ Object hashedObject = typingMapper .reader ().forType (Object .class )
243+ .readValue (unflattenedHashedBytes );;
244+
245+ return hashedObject ;
238246 }
239247
240248 return typingMapper .treeToValue (untypedMapper .valueToTree (hash ), Object .class );
@@ -248,31 +256,37 @@ public Object fromHash(Map<String, Object> hash) {
248256 private Map <String , Object > doUnflatten (Map <String , Object > source ) {
249257
250258 Map <String , Object > result = new LinkedHashMap <>();
251- Set <String > treatSeperate = new LinkedHashSet <>();
259+ Set <String > treatSeparate = new LinkedHashSet <>();
260+
252261 for (Entry <String , Object > entry : source .entrySet ()) {
253262
254263 String key = entry .getKey ();
255- String [] args = key .split ("\\ ." );
264+ String [] keyParts = key .split ("\\ ." );
256265
257- if (args .length == 1 && ! args [0 ]. contains ( "[" )) {
266+ if (keyParts .length == 1 && isNotIndexed ( keyParts [0 ])) {
258267 result .put (entry .getKey (), entry .getValue ());
259268 continue ;
260269 }
261270
262- if (args .length == 1 && args [0 ]. contains ( "[" )) {
271+ if (keyParts .length == 1 && isIndexed ( keyParts [0 ])) {
263272
264- String prunedKey = args [0 ].substring (0 , args [0 ].indexOf ('[' ));
265- if (result .containsKey (prunedKey )) {
266- appendValueToTypedList (args [0 ], entry .getValue (), (List <Object >) result .get (prunedKey ));
267- } else {
268- result .put (prunedKey , createTypedListWithValue (entry .getValue ()));
273+ String indexedKeyName = keyParts [0 ];
274+ String nonIndexedKeyName = stripIndex (indexedKeyName );
275+
276+ int index = getIndex (indexedKeyName );
277+
278+ if (result .containsKey (nonIndexedKeyName )) {
279+ addValueToTypedListAtIndex ((List <Object >) result .get (nonIndexedKeyName ), index , entry .getValue ());
280+ }
281+ else {
282+ result .put (nonIndexedKeyName , createTypedListWithValue (index , entry .getValue ()));
269283 }
270284 } else {
271- treatSeperate .add (key .substring (0 , key .indexOf ('.' )));
285+ treatSeparate .add (key .substring (0 , key .indexOf ('.' )));
272286 }
273287 }
274288
275- for (String partial : treatSeperate ) {
289+ for (String partial : treatSeparate ) {
276290
277291 Map <String , Object > newSource = new LinkedHashMap <>();
278292
@@ -284,12 +298,13 @@ private Map<String, Object> doUnflatten(Map<String, Object> source) {
284298
285299 if (partial .endsWith ("]" )) {
286300
287- String prunedKey = partial .substring (0 , partial .indexOf ('[' ));
301+ String nonIndexPartial = stripIndex (partial );
302+ int index = getIndex (partial );
288303
289- if (result .containsKey (prunedKey )) {
290- appendValueToTypedList ( partial , doUnflatten ( newSource ), ( List <Object >) result .get (prunedKey ));
304+ if (result .containsKey (nonIndexPartial )) {
305+ addValueToTypedListAtIndex (( List <Object >) result .get (nonIndexPartial ), index , doUnflatten ( newSource ));
291306 } else {
292- result .put (prunedKey , createTypedListWithValue (doUnflatten (newSource )));
307+ result .put (nonIndexPartial , createTypedListWithValue (index , doUnflatten (newSource )));
293308 }
294309 } else {
295310 result .put (partial , doUnflatten (newSource ));
@@ -299,6 +314,27 @@ private Map<String, Object> doUnflatten(Map<String, Object> source) {
299314 return result ;
300315 }
301316
317+ private boolean isIndexed (@ NonNull String value ) {
318+ return value .indexOf ('[' ) > -1 ;
319+ }
320+
321+ private boolean isNotIndexed (@ NonNull String value ) {
322+ return !isIndexed (value );
323+ }
324+
325+ private int getIndex (@ NonNull String indexedValue ) {
326+ return Integer .parseInt (indexedValue .substring (indexedValue .indexOf ('[' ) + 1 , indexedValue .length () - 1 ));
327+ }
328+
329+ private @ NonNull String stripIndex (@ NonNull String indexedValue ) {
330+
331+ int indexOfLeftBracket = indexedValue .indexOf ("[" );
332+
333+ return indexOfLeftBracket > -1
334+ ? indexedValue .substring (0 , indexOfLeftBracket )
335+ : indexedValue ;
336+ }
337+
302338 private Map <String , Object > flattenMap (Iterator <Entry <String , JsonNode >> source ) {
303339
304340 Map <String , Object > resultMap = new HashMap <>();
@@ -314,7 +350,6 @@ private void doFlatten(String propertyPrefix, Iterator<Entry<String, JsonNode>>
314350 }
315351
316352 while (inputMap .hasNext ()) {
317-
318353 Entry <String , JsonNode > entry = inputMap .next ();
319354 flattenElement (propertyPrefix + entry .getKey (), entry .getValue (), resultMap );
320355 }
@@ -323,7 +358,6 @@ private void doFlatten(String propertyPrefix, Iterator<Entry<String, JsonNode>>
323358 private void flattenElement (String propertyPrefix , Object source , Map <String , Object > resultMap ) {
324359
325360 if (!(source instanceof JsonNode )) {
326-
327361 resultMap .put (propertyPrefix , source );
328362 return ;
329363 }
@@ -337,6 +371,7 @@ private void flattenElement(String propertyPrefix, Object source, Map<String, Ob
337371 while (nodes .hasNext ()) {
338372
339373 JsonNode cur = nodes .next ();
374+
340375 if (cur .isArray ()) {
341376 this .flattenCollection (propertyPrefix , cur .elements (), resultMap );
342377 } else {
@@ -370,12 +405,13 @@ private void flattenElement(String propertyPrefix, Object source, Map<String, Ob
370405
371406 try {
372407 resultMap .put (propertyPrefix , next .binaryValue ());
373- } catch (IOException e ) {
374- throw new IllegalStateException (String .format ("Cannot read binary value of '%s'" , propertyPrefix ), e );
408+ } catch (IOException cause ) {
409+ String message = String .format ("Cannot read binary value of '%s'" , propertyPrefix );
410+ throw new IllegalStateException (message , cause );
375411 }
412+
376413 break ;
377414 }
378-
379415 }
380416 }
381417 }
@@ -390,53 +426,49 @@ private void flattenElement(String propertyPrefix, Object source, Map<String, Ob
390426 private boolean mightBeJavaType (JsonNode node ) {
391427
392428 String textValue = node .asText ();
393- if (!SOURCE_VERSION_PRESENT ) {
394-
395- if (ObjectUtils .nullSafeEquals (textValue , "java.util.Date" )) {
396- return true ;
397- }
398- if (ObjectUtils .nullSafeEquals (textValue , "java.math.BigInteger" )) {
399- return true ;
400- }
401- if (ObjectUtils .nullSafeEquals (textValue , "java.math.BigDecimal" )) {
402- return true ;
403- }
404429
405- return false ;
430+ if (!SOURCE_VERSION_PRESENT ) {
431+ return Arrays .asList ("java.util.Date" , "java.math.BigInteger" , "java.math.BigDecimal" ).contains (textValue );
406432 }
407- return javax .lang .model .SourceVersion .isName (textValue );
408433
434+ return javax .lang .model .SourceVersion .isName (textValue );
409435 }
410436
411437 private void flattenCollection (String propertyPrefix , Iterator <JsonNode > list , Map <String , Object > resultMap ) {
412438
413- int counter = 0 ;
414- while (list .hasNext ()) {
439+ for (int counter = 0 ; list .hasNext (); counter ++) {
415440 JsonNode element = list .next ();
416441 flattenElement (propertyPrefix + "[" + counter + "]" , element , resultMap );
417- counter ++;
418442 }
419443 }
420444
421445 @ SuppressWarnings ("unchecked" )
422- private void appendValueToTypedList ( String key , Object value , List < Object > destination ) {
446+ private void addValueToTypedListAtIndex ( List < Object > listWithTypeHint , int index , Object value ) {
423447
424- int index = Integer .parseInt (key .substring (key .indexOf ('[' ) + 1 , key .length () - 1 ));
425- List <Object > resultList = ((List <Object >) destination .get (1 ));
426- if (resultList .size () < index ) {
427- resultList .add (value );
428- } else {
429- resultList .add (index , value );
448+ List <Object > valueList = (List <Object >) listWithTypeHint .get (1 );
449+
450+ if (index >= valueList .size ()) {
451+ int initialCapacity = index + 1 ;
452+ List <Object > newValueList = new ArrayList <>(initialCapacity );
453+ Collections .copy (CollectionUtils .initializeList (newValueList , initialCapacity ), valueList );
454+ listWithTypeHint .set (1 , newValueList );
455+ valueList = newValueList ;
430456 }
457+
458+ valueList .set (index , value );
431459 }
432460
433- private List <Object > createTypedListWithValue (Object value ) {
461+ private List <Object > createTypedListWithValue (int index , Object value ) {
462+
463+ int initialCapacity = index + 1 ;
464+
465+ List <Object > valueList = CollectionUtils .initializeList (new ArrayList <>(initialCapacity ), initialCapacity );
466+ valueList .set (index , value );
434467
435468 List <Object > listWithTypeHint = new ArrayList <>();
436- listWithTypeHint .add (ArrayList .class .getName ()); // why jackson? why?
437- List <Object > values = new ArrayList <>();
438- values .add (value );
439- listWithTypeHint .add (values );
469+ listWithTypeHint .add (ArrayList .class .getName ());
470+ listWithTypeHint .add (valueList );
471+
440472 return listWithTypeHint ;
441473 }
442474
@@ -468,16 +500,16 @@ public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, Typ
468500 @ Override
469501 public Date deserialize (JsonParser p , DeserializationContext ctxt ) throws IOException {
470502
471- Object val = delegate .deserialize (p , ctxt );
503+ Object value = delegate .deserialize (p , ctxt );
472504
473- if (val instanceof Date ) {
474- return (Date ) val ;
505+ if (value instanceof Date ) {
506+ return (Date ) value ;
475507 }
476508
477509 try {
478- return ctxt .getConfig ().getDateFormat ().parse (val .toString ());
479- } catch (ParseException e ) {
480- return new Date (NumberUtils .parseNumber (val .toString (), Long .class ));
510+ return ctxt .getConfig ().getDateFormat ().parse (value .toString ());
511+ } catch (ParseException cause ) {
512+ return new Date (NumberUtils .parseNumber (value .toString (), Long .class ));
481513 }
482514 }
483515 }
@@ -500,13 +532,13 @@ public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IO
500532
501533 Date date = dateDeserializer .deserialize (p , ctxt );
502534
503- if (date == null ) {
504- return null ;
535+ if (date != null ) {
536+ Calendar calendar = Calendar .getInstance ();
537+ calendar .setTime (date );
538+ return calendar ;
505539 }
506540
507- Calendar calendar = Calendar .getInstance ();
508- calendar .setTime (date );
509- return calendar ;
541+ return null ;
510542 }
511543 }
512544
@@ -524,17 +556,20 @@ private static class UntypedSerializer<T> extends JsonSerializer<T> {
524556 }
525557
526558 @ Override
527- public void serializeWithType (T value , JsonGenerator gen , SerializerProvider serializers , TypeSerializer typeSer )
528- throws IOException {
529- serialize (value , gen , serializers );
559+ public void serializeWithType (T value , JsonGenerator jsonGenerator , SerializerProvider serializers ,
560+ TypeSerializer typeSerializer ) throws IOException {
561+
562+ serialize (value , jsonGenerator , serializers );
530563 }
531564
532565 @ Override
533- public void serialize (T value , JsonGenerator gen , SerializerProvider serializers ) throws IOException {
534- if (value == null ) {
535- serializers .defaultSerializeNull (gen );
566+ public void serialize (@ Nullable T value , JsonGenerator jsonGenerator , SerializerProvider serializers )
567+ throws IOException {
568+
569+ if (value != null ) {
570+ delegate .serialize (value , jsonGenerator , serializers );
536571 } else {
537- delegate . serialize ( value , gen , serializers );
572+ serializers . defaultSerializeNull ( jsonGenerator );
538573 }
539574 }
540575 }
0 commit comments