4545import org .springframework .data .mapping .PersistentPropertyAccessor ;
4646import org .springframework .data .mapping .PreferredConstructor ;
4747import org .springframework .data .mapping .PropertyHandler ;
48+ import org .springframework .data .mapping .model .MappingException ;
4849import org .springframework .data .mapping .model .PersistentEntityParameterValueProvider ;
4950import org .springframework .data .mapping .model .PropertyValueProvider ;
5051import org .springframework .data .redis .core .index .Indexed ;
103104 */
104105public class MappingRedisConverter implements RedisConverter , InitializingBean {
105106
107+ private static final String TYPE_HINT_ALIAS = "_class" ;
108+
106109private final RedisMappingContext mappingContext ;
107110private final GenericConversionService conversionService ;
108111private final EntityInstantiators entityInstantiators ;
@@ -247,9 +250,9 @@ else if (persistentProperty.isCollectionLike()) {
247250
248251RedisData source = new RedisData (bucket );
249252
250- byte [] type = bucket .get (currentPath + "._class" );
253+ byte [] type = bucket .get (currentPath + "." + TYPE_HINT_ALIAS );
251254if (type != null && type .length > 0 ) {
252- source .getBucket ().put ("_class" , type );
255+ source .getBucket ().put (TYPE_HINT_ALIAS , type );
253256}
254257
255258accessor .setProperty (persistentProperty , readInternal (currentPath , targetType , source ));
@@ -265,8 +268,8 @@ else if (persistentProperty.isCollectionLike()) {
265268}
266269}
267270
268- accessor . setProperty ( persistentProperty ,
269- fromBytes (source .getBucket ().get (currentPath ), persistentProperty . getActualType () ));
271+ Class <?> typeToUse = getTypeHint ( currentPath , source . getBucket (), persistentProperty . getActualType ());
272+ accessor . setProperty ( persistentProperty , fromBytes (source .getBucket ().get (currentPath ), typeToUse ));
270273}
271274}
272275
@@ -376,16 +379,17 @@ private void writeInternal(final String keyspace, final String path, final Objec
376379
377380if (customConversions .hasCustomWriteTarget (value .getClass ())) {
378381
379- if (customConversions .getCustomWriteTarget (value .getClass ()).equals (byte [].class )) {
382+ if (! StringUtils . hasText ( path ) && customConversions .getCustomWriteTarget (value .getClass ()).equals (byte [].class )) {
380383sink .getBucket ().put (StringUtils .hasText (path ) ? path : "_raw" , conversionService .convert (value , byte [].class ));
381384} else {
382- writeToBucket (path , value , sink );
385+ writeToBucket (path , value , sink , typeHint . getType () );
383386}
384387return ;
385388}
386389
387390if (value .getClass () != typeHint .getType ()) {
388- sink .getBucket ().put ((!path .isEmpty () ? path + "._class" : "_class" ), toBytes (value .getClass ().getName ()));
391+ sink .getBucket ().put ((!path .isEmpty () ? path + "." + TYPE_HINT_ALIAS : TYPE_HINT_ALIAS ),
392+ toBytes (value .getClass ().getName ()));
389393}
390394
391395final KeyValuePersistentEntity <?> entity = mappingContext .getPersistentEntity (value .getClass ());
@@ -416,7 +420,7 @@ public void doWithPersistentProperty(KeyValuePersistentProperty persistentProper
416420} else {
417421
418422Object propertyValue = accessor .getProperty (persistentProperty );
419- sink . getBucket (). put ( propertyStringPath , toBytes ( propertyValue ));
423+ writeToBucket ( propertyStringPath , propertyValue , sink , persistentProperty . getType ( ));
420424}
421425}
422426});
@@ -494,15 +498,15 @@ private void writeCollection(String keyspace, String path, Collection<?> values,
494498String currentPath = path + ".[" + i + "]" ;
495499
496500if (customConversions .hasCustomWriteTarget (value .getClass ())) {
497- writeToBucket (currentPath , value , sink );
501+ writeToBucket (currentPath , value , sink , typeHint . getType () );
498502} else {
499503writeInternal (keyspace , currentPath , value , typeHint , sink );
500504}
501505i ++;
502506}
503507}
504508
505- private void writeToBucket (String path , Object value , RedisData sink ) {
509+ private void writeToBucket (String path , Object value , RedisData sink , Class <?> propertyType ) {
506510
507511if (value == null ) {
508512return ;
@@ -512,6 +516,12 @@ private void writeToBucket(String path, Object value, RedisData sink) {
512516
513517Class <?> targetType = customConversions .getCustomWriteTarget (value .getClass ());
514518
519+ if (!ClassUtils .isAssignable (Map .class , targetType ) && customConversions .isSimpleType (value .getClass ())
520+ && value .getClass () != propertyType ) {
521+ sink .getBucket ().put ((!path .isEmpty () ? path + "." + TYPE_HINT_ALIAS : TYPE_HINT_ALIAS ),
522+ toBytes (value .getClass ().getName ()));
523+ }
524+
515525if (ClassUtils .isAssignable (Map .class , targetType )) {
516526
517527Map <?, ?> map = (Map <?, ?>) conversionService .convert (value , targetType );
@@ -547,7 +557,13 @@ private Collection<?> readCollectionOfSimpleTypes(String path, Class<?> collecti
547557Collection <Object > target = CollectionFactory .createCollection (collectionType , valueType , partial .size ());
548558
549559for (String key : keys ) {
550- target .add (fromBytes (partial .get (key ), valueType ));
560+
561+ if (key .endsWith (TYPE_HINT_ALIAS )) {
562+ continue ;
563+ }
564+
565+ Class <?> typeToUse = getTypeHint (key , source .getBucket (), valueType );
566+ target .add (fromBytes (partial .get (key ), typeToUse ));
551567}
552568
553569return target ;
@@ -572,9 +588,9 @@ private Collection<?> readCollectionOfComplexTypes(String path, Class<?> collect
572588
573589Bucket elementData = source .extract (key );
574590
575- byte [] typeInfo = elementData .get (key + "._class" );
591+ byte [] typeInfo = elementData .get (key + "." + TYPE_HINT_ALIAS );
576592if (typeInfo != null && typeInfo .length > 0 ) {
577- elementData .put ("_class" , typeInfo );
593+ elementData .put (TYPE_HINT_ALIAS , typeInfo );
578594}
579595
580596Object o = readInternal (key , valueType , new RedisData (elementData ));
@@ -606,7 +622,7 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
606622String currentPath = path + ".[" + entry .getKey () + "]" ;
607623
608624if (customConversions .hasCustomWriteTarget (entry .getValue ().getClass ())) {
609- writeToBucket (currentPath , entry .getValue (), sink );
625+ writeToBucket (currentPath , entry .getValue (), sink , mapValueType );
610626} else {
611627writeInternal (keyspace , currentPath , entry .getValue (), ClassTypeInformation .from (mapValueType ), sink );
612628}
@@ -630,6 +646,10 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
630646
631647for (Entry <String , byte []> entry : partial .entrySet ()) {
632648
649+ if (entry .getKey ().endsWith (TYPE_HINT_ALIAS )) {
650+ continue ;
651+ }
652+
633653String regex = "^(" + Pattern .quote (path ) + "\\ .\\ [)(.*?)(\\ ])" ;
634654Pattern pattern = Pattern .compile (regex );
635655
@@ -639,7 +659,9 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
639659String .format ("Cannot extract map value for key '%s' in path '%s'." , entry .getKey (), path ));
640660}
641661String key = matcher .group (2 );
642- target .put (key , fromBytes (entry .getValue (), valueType ));
662+
663+ Class <?> typeToUse = getTypeHint (path + ".[" + key + "]" , source .getBucket (), valueType );
664+ target .put (key , fromBytes (entry .getValue (), typeToUse ));
643665}
644666
645667return target ;
@@ -674,9 +696,9 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
674696
675697Bucket partial = source .getBucket ().extract (key );
676698
677- byte [] typeInfo = partial .get (key + "._class" );
699+ byte [] typeInfo = partial .get (key + "." + TYPE_HINT_ALIAS );
678700if (typeInfo != null && typeInfo .length > 0 ) {
679- partial .put ("_class" , typeInfo );
701+ partial .put (TYPE_HINT_ALIAS , typeInfo );
680702}
681703
682704Object o = readInternal (key , valueType , new RedisData (partial ));
@@ -686,6 +708,24 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
686708return target ;
687709}
688710
711+ private Class <?> getTypeHint (String path , Bucket bucket , Class <?> fallback ) {
712+
713+ byte [] typeInfo = bucket .get (path + "." + TYPE_HINT_ALIAS );
714+
715+ if (typeInfo == null || typeInfo .length < 1 ) {
716+ return fallback ;
717+ }
718+
719+ String typeName = fromBytes (typeInfo , String .class );
720+ try {
721+ return ClassUtils .forName (typeName , this .getClass ().getClassLoader ());
722+ } catch (ClassNotFoundException e ) {
723+ throw new MappingException (String .format ("Cannot find class for type %s. " , typeName ), e );
724+ } catch (LinkageError e ) {
725+ throw new MappingException (String .format ("Cannot find class for type %s. " , typeName ), e );
726+ }
727+ }
728+
689729/**
690730 * Convert given source to binary representation using the underlying {@link ConversionService}.
691731 *
@@ -789,7 +829,7 @@ private static class RedisTypeAliasAccessor implements TypeAliasAccessor<RedisDa
789829private final ConversionService conversionService ;
790830
791831RedisTypeAliasAccessor (ConversionService conversionService ) {
792- this (conversionService , "_class" );
832+ this (conversionService , TYPE_HINT_ALIAS );
793833}
794834
795835RedisTypeAliasAccessor (ConversionService conversionService , String typeKey ) {
0 commit comments