Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion: Spring ConversionService #20

Open
micheljung opened this issue Sep 13, 2023 · 0 comments
Open

Discussion: Spring ConversionService #20

micheljung opened this issue Sep 13, 2023 · 0 comments

Comments

@micheljung
Copy link

micheljung commented Sep 13, 2023

(unfortunately, GitHub discussions are not enabled in this project).

Background

We've been using MapStruct and implemented each mapper as a Spring Converter. Because of MapStruct's issues with Kotlin, some of which seem unresolvable, we're looking for an alternative and came across ShapeShift.

ShapeShift advertises with "Seamless Spring integration", but it's not that seamless; while there is a starter that automatically registers transformers and decorators, mappers still need to be registered explicitly and ShapeShift doesn't integrate well with Spring's Type Conversion (at least I wouldn't know how and it's not documented).

Issues

A Spring Converter converts from type A to type B. In ShapeShift, we have mappers and MapperTransformer that convert from one type to another. While transformers can rather easily be turned into Converter beans (but still not seamlessly), the same can't be said for mappers as there is no "Mapper" type. As a result, you can't create mapper beans and therefore you can't auto-register them.

There's also ConditionalConverter in Spring and MappingCondition in ShapeShift, but I didn't check if they integrate well.

I tried implementing some adapter code but it seemed impossible. Here's my failed attempt:

import dev.krud.shapeshift.MappingStrategy
import dev.krud.shapeshift.ShapeShift
import dev.krud.shapeshift.ShapeShiftBuilder
import dev.krud.shapeshift.spring.ShapeShiftBuilderCustomizer
import dev.krud.shapeshift.transformer.base.MappingTransformer
import dev.krud.shapeshift.transformer.base.MappingTransformerContext
import org.springframework.context.annotation.Configuration
import org.springframework.core.convert.TypeDescriptor
import org.springframework.core.convert.converter.Converter
import org.springframework.core.convert.converter.GenericConverter
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair

/**
 * Implements both, ShapeShift's [MappingTransformer] as well as Spring's [Converter]. This
 * abstracts ShapeShift away from subclasses, so that they can implement Spring's [Converter]
 * instead.
 */
abstract class MappingTransformerConverter<FROM, TO> : Converter<FROM, TO>,
  MappingTransformer<FROM, TO> {

  final override fun transform(context: MappingTransformerContext<out FROM>) =
    context.originalValue?.let { convert(it) }
}

@Configuration
class MyShapeShiftBuilderCustomizer(
  // There seems to be no way to get mapper beans
  private val mappers: Iterable<NonExistantMapperType>,
) : ShapeShiftBuilderCustomizer {
  override fun customize(builder: ShapeShiftBuilder) {
    builder
      .withDefaultMappingStrategy(MappingStrategy.MAP_ALL)

    // This is not valid code
    mappers.forEach { builder.withMapping(it) }
  }
}

/** A Spring [GenericConverter] that delegates conversion to [ShapeShift]. */
class ShapeShiftConverter(
  private val shapeShift: ShapeShift,
) : GenericConverter {

  override fun getConvertibleTypes(): Set<ConvertiblePair> {
    // There seems to be no way to get these from ShapeShift
    return emptySet()
  }

  override fun convert(source: Any?, sourceType: TypeDescriptor, targetType: TypeDescriptor): Any? =
    source?.let { shapeShift.map(it, targetType.objectType) }
}

What are your thoughts on this? Is this something you would like to support? Can it be done already but I just didn't see it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant