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

Any way to make the serializers not part of public API #2108

Closed
BoD opened this issue Nov 24, 2022 · 8 comments
Closed

Any way to make the serializers not part of public API #2108

BoD opened this issue Nov 24, 2022 · 8 comments
Labels

Comments

@BoD
Copy link

BoD commented Nov 24, 2022

We've started to use kotlinx.serialization on some of our public classes, and that makes them expose new fields/methods/classes for the serializers, quite understandably. See this PR for instance, which shows the API diff via binary-compatibility-validator.
We were wondering if there could be any way to make those generated in a less public way (internal maybe?).

This is probably a long shot but we figured we should at least ask!

@sandwwraith
Copy link
Member

Given that your classes are public, it makes sense if their serializers are also public. By declaring a public class as serializable, you agree that anyone can use it in their own @Serializable class, and the serializer needs to be accessible for that. What you are describing here will lead to a 'public class, but serializable only privately', which is a complex situation the serialization plugin wasn't designed for — it assumes if the class is public and has @Serializable annotation, there's a public serializer somewhere. Just think of these declarations as some synthetic internal stuff (there are a lot of things like that in the API dump, actually)

If your concern is binary compatibility for serializers, we are well aware of this situation and do not make changes to it until it is absolutely necessary, so you can be sure that @Serializable class Bar(val foo: Foo) / @Serializable class Foo will remain compatible and serializable even if compiled by different Kotlin and serialization plugin versions.

@BoD
Copy link
Author

BoD commented Nov 25, 2022

Thanks a lot for the clarification, that makes sense.

@BoD BoD closed this as completed Nov 25, 2022
@martinbonnin
Copy link
Contributor

What you are describing here will lead to a 'public class, but serializable only privately', which is a complex situation the serialization plugin wasn't designed for

Any chance this could be supported in the future?

The use case is to hide the serialization format from callers. For an example, an initial version of a lib could use Json for readability/convenience but then switch to protobuf for performance later down the road. By making the serialization format public, these kind of use cases are a lot harder to address.

@martinbonnin
Copy link
Contributor

Another use case is to make kotlinx.serialization an "implementation" dependency and not "api" for libs that use it. Using "implementation" instead of "api" opens the door to build optimizations and prevents unneeded symbols to leak in the IDE.

@pdvrieze
Copy link
Contributor

pdvrieze commented Dec 7, 2022

The use case is to hide the serialization format from callers. For an example, an initial version of a lib could use Json for readability/convenience but then switch to protobuf for performance later down the road. By making the serialization format public, these kind of use cases are a lot harder to address.

One thing to keep in mind is the fact that the format is not actually part of the serializer at all. You use the same serializer for protobuf, json and other formats. Formats may have additional annotations that are interpreted in a format specific way. Those annotations are also public (and part of the serialdescriptor), but shouldn't really be interpreted outside the format.

@sandwwraith
Copy link
Member

@pdvrieze is absolutely right. The format is not a part of a public API; generated serializer does not depend on it. You may even provide a library with serializable classes without dependency on any actual format — using kotlinx-serialization-core artifact instead of json.

Regarding implementation dependency — I may be wrong, but you would need it any way to read @Serializable annotation.

@martinbonnin
Copy link
Contributor

The format is not a part of a public API;

TIL, thanks! I think the argument still holds for the serialization mecanism. If I switch to protobuf, maybe I want to use wire or something else. Putting the serializer in the API exposes unnecessary implementation details.

you would need it any way to read @serializable annotation

The consuming project doesn't need to access @Serializable. It need access to a single serialize(BufferedSource) method (and symmetrically deserialize if needed) :

@Serializable
class SomePublicClass {
  val prop1: String

  val prop2: String

  // ... more props

  fun serialize(source: BufferedSource) {
    // implementation detail
  }
}

I understand this is not top priority but I think there are legit reasons to hide the implementation details while still keeping the class public.

@martinbonnin
Copy link
Contributor

The consuming project doesn't need to access @Serializable

To be more precise here, the app consuming project has @Serializable as a bytecode annotation in the lib public class but the compiler doesn't need to have @Serializable in the app compile classpath. I made a test here

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

4 participants