Consider custom converters in ConversionContext before applying collection converters.

We now inspect whether custom conversions should be applied before reading a value into a Map or Collection to consider converters for Map-like and Collection-like types.

Closes #1181
This commit is contained in:
Mark Paluch
2021-10-18 09:16:01 +02:00
parent 4bac2a159e
commit f28b200d57
2 changed files with 63 additions and 2 deletions

View File

@@ -144,7 +144,7 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
*/
protected ConversionContext getConversionContext() {
return new ConversionContext(this::doReadRow, this::doReadTupleValue, this::doReadUdtValue,
return new ConversionContext(getCustomConversions(), this::doReadRow, this::doReadTupleValue, this::doReadUdtValue,
this::readCollectionOrArray, this::readMap, this::getPotentiallyConvertedSimpleRead);
}
@@ -1161,6 +1161,8 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
*/
protected static class ConversionContext {
private final org.springframework.data.convert.CustomConversions conversions;
private final ContainerValueConverter<Row> rowConverter;
private final ContainerValueConverter<TupleValue> tupleConverter;
@@ -1173,10 +1175,12 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
private final ValueConverter<Object> elementConverter;
public ConversionContext(ContainerValueConverter<Row> rowConverter,
public ConversionContext(org.springframework.data.convert.CustomConversions conversions,
ContainerValueConverter<Row> rowConverter,
ContainerValueConverter<TupleValue> tupleConverter, ContainerValueConverter<UdtValue> udtConverter,
ContainerValueConverter<Collection<?>> collectionConverter, ContainerValueConverter<Map<?, ?>> mapConverter,
ValueConverter<Object> elementConverter) {
this.conversions = conversions;
this.rowConverter = rowConverter;
this.tupleConverter = tupleConverter;
this.udtConverter = udtConverter;
@@ -1197,6 +1201,10 @@ public class MappingCassandraConverter extends AbstractCassandraConverter
Assert.notNull(typeHint, "TypeInformation must not be null");
if (conversions.hasCustomReadTarget(source.getClass(), typeHint.getType())) {
return (S) elementConverter.convert(source, typeHint);
}
if (source instanceof Collection) {
Class<?> rawType = typeHint.getType();

View File

@@ -38,10 +38,14 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.*;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Transient;
@@ -1445,4 +1449,53 @@ public class MappingCassandraConverterUnitTests {
WithNullableEmbeddedType target = mappingCassandraConverter.read(WithNullableEmbeddedType.class, source);
assertThat(target.nested).isNull();
}
@Test // DATACASS-1181
void shouldApplyCustomConverterToMapLikeType() {
CassandraCustomConversions conversions = new CassandraCustomConversions(
Arrays.asList(JsonToStringConverter.INSTANCE, StringToJsonConverter.INSTANCE));
this.mappingContext = new CassandraMappingContext();
this.mappingContext.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
this.mappingCassandraConverter = new MappingCassandraConverter(mappingContext);
this.mappingCassandraConverter.setCustomConversions(conversions);
this.mappingCassandraConverter.afterPropertiesSet();
Row source = RowMockUtil.newRowMock(column("thejson", "{\"hello\":\"world\"}", DataTypes.TEXT));
TypeWithJsonObject target = mappingCassandraConverter.read(TypeWithJsonObject.class, source);
assertThat(target.theJson).isNotNull();
assertThat(target.theJson.get("hello")).isEqualTo("world");
}
static class TypeWithJsonObject {
JSONObject theJson;
}
enum StringToJsonConverter implements Converter<String, JSONObject> {
INSTANCE;
@Override
public JSONObject convert(String source) {
try {
return (JSONObject) new JSONParser().parse(source);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
enum JsonToStringConverter implements Converter<JSONObject, String> {
INSTANCE;
@Override
public String convert(JSONObject source) {
return source.toJSONString();
}
}
}