Home > Back-end >  Map Kotlin data objects to data objects with enum attributes
Map Kotlin data objects to data objects with enum attributes

Time:09-17

I have the following function that should map from one data class to another but it is not working:

private fun ClassA.toClassB() = ClassB(

  text = text,
  meta = meta,
  message = message)

ClassB is underlined in red with the error None of the following functions can be called with the arguments supplied.

However, just mapping the attribute text works fine:

private fun ClassA.toClassB() = ClassB(

  text = text)

So I guess the upper code doesn't work because of the enum attributes.

ClassA and ClassB look exactly the same, like this:

data class ClassB(
  @JsonProperty("text")
  val text: String,
  @JsonProperty("meta")
  val meta: Meta?,
  @JsonProperty("message")
  val message: Message?,
) {

  constructor(text: String) : this(
    text = text,
    meta = null,
    message = null,
  )


  enum class Meta {
    ONE, TWO, THREE
  }

  enum class Message {
    ONE, TWO
  }}

So how can I map data classes that use enum attributes?

CodePudding user response:

Your Error

"ClassA and ClassB look exactly the same" -> This results in "duplicate" enums:

Meta & Message exist both in ClassA and in ClassB

This leads to the following error:

private fun ClassA.toClassB() = ClassB(
  text = text, // ok
  meta = meta, // expected: ClassB.Meta, provided: ClassA.Meta
  message = message // expected: ClassB.Message, provided: ClassA.Message
)

You now have two options:


You only really need one enum

  • move one somewhere toplevel
  • remove the other one
  • make sure you fix your imports

You actually need two different enums

e.g.

// ClassA
enum class Meta {
    ONE, TWO, THREE
  }

// ClassB
enum class Meta {
    UNO, DOS, TRES
  }

Then i suggest you create a function like:

private fun ClassA.Meta.toClassBMeta() =
    when(this){
        ClassA.Meta.ONE -> ClassB.Meta.UNO,
        ClassA.Meta.TWO -> ClassB.Meta.DOS,
        ClassA.Meta.THREE -> ClassB.Meta.TRES,
    }

Use it in your mapper:

private fun ClassA.toClassB() = ClassB(
  text = text,
  meta = meta.toClassBMeta(),
  message = message.toClassBMessage())

CodePudding user response:

I think your problem lies in the way you have defined your enums. It seems in your code snippet that you defined them within the scope of ClassB. You stated the contents of both ClassA and ClassB are identical, so I'm assuming you defined the enums in both. This is where your problem lies.

When compiling, every object creates a sort of "signature" as a unique identifier, so that the compiler knows ClassA from ClassB. Since you define the enums in both classes, they too get unique signatures.

So, instead of having 2 signatures for the enums, you have 4. The compiler tries cloning it (which is essentially what you're doing), but fails because it tries to directly map to an enum with a different signature.

Quite literally "same same, but different, but still same".

What you should try to do is define the enums outside of the classes.

namespace com.path1
{
  enum class Meta {
    ONE,
    TWO,
    THREE
  }

  enum class Message {
    ONE,
    TWO
  }

  data class ClassA(
    @JsonProperty("text")
      val text: String,
    @JsonProperty("meta")
      val meta: Meta? = null,
    @JsonProperty("message")
      val message: Message? = null
  ) {}
}

namespace com.path2 {
  import com.path1.Meta
  import com.path2.Message

  data class ClassB(
    @JsonProperty("text")
      val text: String,
    @JsonProperty("meta")
      val meta: Meta? = null,
    @JsonProperty("message")
      val message: Message? = null
  ) {}
}

Above code will compile only 2 signatures for the enums. Assuming the contents of your classes remain identical, they will now both use the same signature for the enum classes.

Also notice how I deleted your constructor. If you want every parameter to be instantiable by default, omit defining a constructor completely, as the way you defined the properties in the class form a constructor already.

  • Related