diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java index 3ec4cb01..684e746c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java @@ -42,24 +42,24 @@ import java.util.*; /** * The Couchbase special {@link MappingCouchbaseConverter}. - * + *

* This converter is responsible for mapping (read and writing) value from and to target formats. * * @author Michael Nitschinger */ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter - implements ApplicationContextAware { + implements ApplicationContextAware { protected ApplicationContext applicationContext; protected final MappingContext, - CouchbasePersistentProperty> mappingContext; + CouchbasePersistentProperty> mappingContext; protected boolean useFieldAccessOnly = true; protected CouchbaseTypeMapper typeMapper; private SpELContext spELContext; @SuppressWarnings("deprecation") public MappingCouchbaseConverter(MappingContext, - CouchbasePersistentProperty> mappingContext) { + CouchbasePersistentProperty> mappingContext) { super(ConversionServiceFactory.createDefaultConversionService()); this.mappingContext = mappingContext; @@ -70,13 +70,13 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter @Override public MappingContext, - CouchbasePersistentProperty> getMappingContext() { + CouchbasePersistentProperty> getMappingContext() { return mappingContext; } @Override public R read(Class clazz, CouchbaseDocument doc) { - return read(ClassTypeInformation.from(clazz), doc, null); + return read(ClassTypeInformation.from(clazz), doc, null); } protected R read(TypeInformation type, CouchbaseDocument doc) { @@ -97,7 +97,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } CouchbasePersistentEntity persistentEntity = (CouchbasePersistentEntity) - mappingContext.getPersistentEntity(typeToUse); + mappingContext.getPersistentEntity(typeToUse); if (persistentEntity == null) { throw new MappingException("No mapping metadata found for " + rawType.getName()); @@ -106,7 +106,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter return read(persistentEntity, source, parent); } - protected R read(final CouchbasePersistentEntity entity, final CouchbaseDocument source, final Object parent) { + protected R read(final CouchbasePersistentEntity entity, final CouchbaseDocument source, final Object parent) { final DefaultSpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator(source, spELContext); ParameterValueProvider provider = getParameterProvider(entity, source, evaluator, parent); @@ -151,13 +151,13 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } private ParameterValueProvider getParameterProvider(CouchbasePersistentEntity entity, - CouchbaseDocument source, DefaultSpELExpressionEvaluator evaluator, Object parent) { + CouchbaseDocument source, DefaultSpELExpressionEvaluator evaluator, Object parent) { CouchbasePropertyValueProvider provider = new CouchbasePropertyValueProvider(source, evaluator, parent); PersistentEntityParameterValueProvider parameterProvider = new PersistentEntityParameterValueProvider( - entity, provider, parent); + entity, provider, parent); return new ConverterAwareSpELExpressionParameterValueProvider(evaluator, conversionService, parameterProvider, - parent); + parent); } protected Map readMap(TypeInformation type, CouchbaseDocument doc, Object parent) { @@ -202,6 +202,14 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter return Enum.valueOf((Class) target, value.toString()); } + if (Class.class.isAssignableFrom(target)) { + try { + return Class.forName(value.toString()); + } catch (ClassNotFoundException e) { + throw new MappingException("Unable to create class from " + value.toString()); + } + } + return target.isAssignableFrom(value.getClass()) ? value : conversionService.convert(value, target); } @@ -211,7 +219,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter return; } - TypeInformation type = ClassTypeInformation.from(source.getClass()); + TypeInformation type = ClassTypeInformation.from(source.getClass()); typeMapper.writeType(type, target); writeInternal(source, target, type); @@ -312,7 +320,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter addCustomTypeKeyIfNecessary(type, source, propertyDoc); CouchbasePersistentEntity entity = isSubtype(prop.getType(), source.getClass()) ? mappingContext - .getPersistentEntity(source.getClass()) : mappingContext.getPersistentEntity(type); + .getPersistentEntity(source.getClass()) : mappingContext.getPersistentEntity(type); writeInternal(source, propertyDoc, entity); target.put(name, propertyDoc); } @@ -324,7 +332,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter return writeMapInternal(map, new CouchbaseDocument(), prop.getTypeInformation()); } - private CouchbaseDocument writeMapInternal(Map source, CouchbaseDocument target, TypeInformation type) { + private CouchbaseDocument writeMapInternal(Map source, CouchbaseDocument target, TypeInformation type) { for (Map.Entry entry : source.entrySet()) { Object key = entry.getKey(); Object val = entry.getValue(); @@ -385,7 +393,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter collectionType = Collection.class.isAssignableFrom(collectionType) ? collectionType : List.class; Collection items = targetType.getType().isArray() ? new ArrayList() : CollectionFactory - .createCollection(collectionType, source.size(false)); + .createCollection(collectionType, source.size(false)); TypeInformation componentType = targetType.getComponentType(); Class rawComponentType = componentType == null ? null : componentType.getType(); @@ -435,7 +443,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter @Override public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { + throws BeansException { this.applicationContext = applicationContext; } @@ -489,11 +497,12 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter } private class ConverterAwareSpELExpressionParameterValueProvider extends - SpELExpressionParameterValueProvider { + SpELExpressionParameterValueProvider { + private final Object parent; public ConverterAwareSpELExpressionParameterValueProvider(SpELExpressionEvaluator evaluator, - ConversionService conversionService, ParameterValueProvider delegate, Object parent) { + ConversionService conversionService, ParameterValueProvider delegate, Object parent) { super(evaluator, conversionService, delegate); this.parent = parent; diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java b/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java index a7e8db9d..2f561e0b 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/translation/JacksonTranslationService.java @@ -87,11 +87,12 @@ public class JacksonTranslationService implements TranslationService { continue; } - if (simpleTypeHolder.isSimpleType(value.getClass()) && !Enum.class.isAssignableFrom(value.getClass())) { + final Class clazz = value.getClass(); + + if (simpleTypeHolder.isSimpleType(clazz) && !isEnumOrClass(clazz)) { generator.writeObject(value); } else { - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(generator, value); + new ObjectMapper().writeValue(generator, value); } } @@ -99,6 +100,10 @@ public class JacksonTranslationService implements TranslationService { generator.writeEndObject(); } + private boolean isEnumOrClass(final Class clazz) { + return Enum.class.isAssignableFrom(clazz) || Class.class.isAssignableFrom(clazz); + } + /** * Decode a JSON string into the {@link CouchbaseStorable} structure. * diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java index b2fc9ecf..fe92bf48 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateTests.java @@ -19,7 +19,6 @@ package org.springframework.data.couchbase.core; import com.couchbase.client.CouchbaseClient; import com.couchbase.client.protocol.views.Query; import com.couchbase.client.protocol.views.Stale; -import com.fasterxml.jackson.annotation.JsonFormat; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +37,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -219,6 +220,16 @@ public class CouchbaseTemplateTests { assertEquals(simpleWithEnum.getType(), SimpleWithEnum.Type.BIG); } + @Test + public void shouldDeserialiseClass() { + SimpleWithClass simpleWithClass = new SimpleWithClass("simpleWithClass:class", Integer.class); + simpleWithClass.setValue("The dish ran away with the spoon."); + template.save(simpleWithClass); + simpleWithClass = template.findById("simpleWithClass:class", SimpleWithClass.class); + assertNotNull(simpleWithClass); + assertThat(simpleWithClass.getValue(), equalTo("The dish ran away with the spoon.")); + } + /** * A sample document with just an id and property. */ @@ -355,4 +366,41 @@ public class CouchbaseTemplateTests { } } + static class SimpleWithClass { + + @Id + private String id; + + private Class integerClass; + + private String value; + + SimpleWithClass(final String id, final Class integerClass) { + this.id = id; + this.integerClass = integerClass; + } + + String getId() { + return id; + } + + void setId(final String id) { + this.id = id; + } + + Class getIntegerClass() { + return integerClass; + } + + void setIntegerClass(final Class integerClass) { + this.integerClass = integerClass; + } + + String getValue() { return value; } + + void setValue(final String value) { + this.value = value; + } + } + }