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

Override JsonContentPolymorphicSerializer with SerializersModule #2340

Closed
todoFixIt opened this issue Jun 21, 2023 · 9 comments
Closed

Override JsonContentPolymorphicSerializer with SerializersModule #2340

todoFixIt opened this issue Jun 21, 2023 · 9 comments
Labels

Comments

@todoFixIt
Copy link

Let's say I have a library that has an interface and an implementation of that interface, that library provides a default serialization for that interface which is the implementation it has. Now I want to use that library and override that serializer since I may have multiple different implementations of that interface and the default serializer is not longer enough, and also I want it to work in the Json SerializersModule construction instead of inline in each call to encode/decode since I will be passing the Json through.

The library code

 @Serializable(with = LibrarySerializer::class)
  interface SomeInterface {
      val someProperty: String
  }

  @Serializable
  class LibraryImpl(
      override val someProperty: String
  ) : SomeInterface
  
  object LibrarySerializer :
      JsonContentPolymorphicSerializer<SomeInterface>(SomeInterface::class) {
      override fun selectDeserializer(element: JsonElement) = LibraryImpl.serializer()
  }

The app code

@Serializable
  class AppImpl(
      override val someProperty: String
  ) : SomeInterface

  object AppSerializer :
      JsonContentPolymorphicSerializer<SomeInterface>(SomeInterface::class) {
      override fun selectDeserializer(element: JsonElement) {
          when {
              someCondition -> AppImpl.serializer()
              else -> LibraryImpl.serializer()
          }
      }
  }

What I would want in the app code

 Json {
      serializersModule = SerializersModule {
          // Something like this that will override the use of 
          // @Serializable(with = LibrarySerializer::class) with the one specified here
          polymorphic(SomeInterface::class, AppSerializer)
      }
  }
@sandwwraith
Copy link
Member

interface with @Serializable(with) is treated the same way as class with custom serializer: this serializer became default. Currently, we do not have provide ways to override default serializer globally. You can use any customization technique we already have: @Serializable on property, @file:UseSerializers, pass serializer in encodeToString explicitly, etc.

Also #2060 (but it's about polymorphic-by-default interfaces)

@todoFixIt
Copy link
Author

Even if you can't override it at least is there any way currently to achieve what I want? that is for the library to provide an implementation (even if not setted) and the client to either use it or create is own, but at the Json level.

@sandwwraith
Copy link
Member

@todoFixIt See this section: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#passing-a-serializer-manually and sections below ('Specifying serializer on a property', 'Specifying serializer for a file', 'Specifying serializer globally using typealias')

@sandwwraith
Copy link
Member

If you do not have further questions, I'll close this one

@todoFixIt
Copy link
Author

Sorry for the delay but I couldn't resolve the issue with the doc you provided, and I think you currently can't, that's why I opened this as a feature (correct me if I'm wrong and there is a way).

I'll show my current simplified code hoping is enough to understand why I mean:

This next code is on a library

interface IApiError {
    val message: String
}
interface IApiResponse {
    val errors: List<IApiError>
}
data class ApiError(override val message): IApiError {}
data class ApiResponse(
    override val errors: List<IApiError>,
) : IApiResponse { }

The next code is on my current project

sealed class NetworkError(open val message: String): IApiError {
    data class Timeout(override val message: String): NetworkError(message)
}

// Some API Call that uses Retrofit and serializes using a Json instance (one for the whole project)
// implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
fun someApiCall(): Call<ApiResponse>

I need to be able to serialize IApiError to ApiError and to NetworkError. The API wont return any kind of discriminator so my current way of serializing it is by using JsonContentPolymorphicSerializer.

The issue is that unless I'm wrong this can only be specified on the @Serialize annotation and that isn't possible if I want both to serialize ApiError and NetWork response since the former is on the library an the later on my project, and I didn't find a way to specify JsonContentPolymorphicSerializer at the Json ¿How would you do resolve this?

@sandwwraith
Copy link
Member

I see. It is a duplicate of #2060 then.

@todoFixIt
Copy link
Author

todoFixIt commented Jul 18, 2023

@sandwwraith Oh I see, thank you, so in 1.6.0 this will be possible, Do you have an estimate of when that would be approximately?

@sandwwraith
Copy link
Member

Unfortunately we have to postpone this after 1.6.0, as this is a large change in behavior that also affects compiler plugin

@todoFixIt
Copy link
Author

Ok, if there is no current workaround I guess you could close this, thank you for your time.

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

No branches or pull requests

2 participants