spring-data-mongodb icon indicating copy to clipboard operation
spring-data-mongodb copied to clipboard

MappingMongoConverter does not work while convert a field which declared with Map<String ,Object >

Open baobinghai opened this issue 4 years ago • 4 comments

In java , version 11..17.RELEASE :

A JSONObject would be recognised as a Map<String,Object> .

It happen to that JSONObject have a BigDecimal field, so i register a couple of converts :

  1. BigDecimalToDecimal128Converter for WritingConverter
  2. Decimal128ToBigDecimalConverter for ReadingConverter

however , BigDecimalToDecimal128Converter for WritingConverter works, but Decimal128ToBigDecimalConverter for ReadingConverter does not work .

it is cased by

 org.springframework.data.mongodb.core.convert.MappingMongoConverter.getPotentiallyConvertedSimpleRead: 

 if (value == null || target == null || target.isAssignableFrom(value.getClass())) {
		return value;
	}

and I also found that i cannot use a customer MappingMongoConverter, because argument ObjectPath in method readMap cannot not be accessed from outside package:

   MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mappingContext) {
        
        @Override
        protected Map<Object, Object> readMap(TypeInformation<?> type, DBObject dbObject, ObjectPath path) {
            return null;
        }
    };

baobinghai avatar Dec 31 '20 10:12 baobinghai

That's a flaw we need to investigate since the Map value type is object and custom converter is registered no type info is written, so on read we cannot determine the desired target type. Maybe there's a way to tell from the type used by the driver itself. We need to investigate options here. If you have a solution in mind (and the time to code it) please feel free to open a PR.

christophstrobl avatar Jan 12 '21 10:01 christophstrobl

Hi @christophstrobl , I tried to reproduce this with a UnitTest, can you validate this is the correct issue?

	@Test
	void shouldConvertBigDecimalToDecimal128AndBack() {
		converter = new MappingMongoConverter(resolver, mappingContext);
		converter.setCustomConversions(MongoCustomConversions.create(it ->
			it.registerConverters(Arrays.asList(new BigDecimalToDecimal128Converter(), new Decimal128ToBigDecimalConverter()))));
		converter.afterPropertiesSet();
		BigDecimal originalValue = BigDecimal.valueOf(10712389.1234d);

		GenericType<Object> source = new GenericType<>();
		source.content = originalValue;

		org.bson.Document writeDocument = new org.bson.Document();
		converter.write(source, writeDocument);
		Decimal128 decimal128Value = new Decimal128(originalValue);
		assertThat(decimal128Value).isEqualTo(writeDocument.get("content"));

		GenericType<Object> observedValue = converter.read(GenericType.class, writeDocument);
		assertThat(observedValue.content).isEqualTo(originalValue); // ---> This Assertion Failed
		assertThat(observedValue.content).isInstanceOf(BigDecimal.class);
	}

Failed Message:

// assertThat(observedValue.content).isEqualTo(originalValue); 

org.opentest4j.AssertionFailedError: 
expected: "10712389.1234 (BigDecimal@4893b344)"
 but was: "10712389.1234 (Decimal128@53a665ad)"
Expected :10712389.1234
Actual   :10712389.1234

Converters:

	@WritingConverter
	private static class BigDecimalToDecimal128Converter implements Converter<BigDecimal, Decimal128> {
		@Override
		public Decimal128 convert(BigDecimal bigDecimal) {
			return new Decimal128(bigDecimal);
		}
	}

	@ReadingConverter
	private static class  Decimal128ToBigDecimalConverter implements Converter<Decimal128, BigDecimal> {
		@Override
		public BigDecimal convert(Decimal128 decimal128) {
			return decimal128.bigDecimalValue();
		}
	}

thekaleidoscope avatar Oct 11 '21 20:10 thekaleidoscope

@christophstrobl @mp911de is this still relevent? if so, can you please validate my understanding above?

thekaleidoscope avatar Oct 18 '21 12:10 thekaleidoscope

@christophstrobl Hello, can you please let me know if my understanding of the issue is correct and if it's still relevant to work on it?

thekaleidoscope avatar Nov 20 '21 13:11 thekaleidoscope

Thanks for providing the test and sorry for long silence. We've been revisiting the issue and the mapping behaves as expected within its boundaries for resolving generic signatures. Since we do not have any data point for resolving the generic type of content in the given example we cannot determine which value to map it to. The writing Converter in turn must not know about the context it is called in. There's actually a couple of ways how to address the issue ranging from a more strongly typed model over, a custom Converter that would add type information, to a property specific ValueConverter.

christophstrobl avatar Mar 10 '23 12:03 christophstrobl

这是来自QQ邮箱的自动回复邮件。   您好,我尽快给您回复。

baobinghai avatar Mar 10 '23 12:03 baobinghai