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

[Kotlin] Kotlin default value support in CompatibleMode #1966

Open
chaokunyang opened this issue Dec 4, 2024 · 1 comment
Open

[Kotlin] Kotlin default value support in CompatibleMode #1966

chaokunyang opened this issue Dec 4, 2024 · 1 comment

Comments

@chaokunyang
Copy link
Collaborator

chaokunyang commented Dec 4, 2024

Feature Request

Kotlin support default value for fields, when deserializing, fury should assign default value to field if serialization didn' have that field and not write value.

For example, this is the class when serialization

data class Foo(val s: String)

val foo = Foo("abc")

And this is the class when deserialization.

data class Foo(val int: Int = 10, val s: String)

If we deserialize the data from data class Foo(val s: String), we will get Foo(int=0, s=abc), but we should return Foo(int=10, s=abc)

Is your feature request related to a problem? Please describe

Serialization:

data class Foo(val s: String )

fun main(args: Array<String>) {
    val fury = Fury.builder().requireClassRegistration(false).withCompatibleMode(CompatibleMode.COMPATIBLE).build()
    Files.write(Paths.get("data"), fury.serialize(Foo(s = "abc")))
}

Deserialization:

data class Foo(val int: Int = 10, val s: String )

fun main(args: Array<String>) {
    val fury = Fury.builder().requireClassRegistration(false).withCompatibleMode(CompatibleMode.COMPATIBLE).build()
    println(fury.deserialize(Files.readAllBytes(Paths.get("data"))))
   // Foo(int=0, s=abc)
}

Describe the solution you'd like

Here is disassembled java code:

public final class Foo {
    private final int int;
    @NotNull
    private final String s;

    public Foo(int n, @NotNull String s) {
        Intrinsics.checkNotNullParameter((Object)s, (String)"s");
        this.int = n;
        this.s = s;
    }

    public /* synthetic */ Foo(int n, String string, int n2, DefaultConstructorMarker defaultConstructorMarker) {
        if ((n2 & 1) != 0) {
            n = 10;
        }
        this(n, string);
    }

    public final int getInt() {
        return this.int;
    }

    @NotNull
    public final String getS() {
        return this.s;
    }

    public final int component1() {
        return this.int;
    }

    @NotNull
    public final String component2() {
        return this.s;
    }

    @NotNull
    public final Foo copy(int n, @NotNull String s) {
        Intrinsics.checkNotNullParameter((Object)s, (String)"s");
        return new Foo(n, s);
    }

    public static /* synthetic */ Foo copy$default(Foo foo, int n, String string, int n2, Object object) {
        if ((n2 & 1) != 0) {
            n = foo.int;
        }
        if ((n2 & 2) != 0) {
            string = foo.s;
        }
        return foo.copy(n, string);
    }

    @NotNull
    public String toString() {
        return "Foo(int=" + this.int + ", s=" + this.s + ')';
    }

    public int hashCode() {
        int result = Integer.hashCode(this.int);
        result = result * 31 + this.s.hashCode();
        return result;
    }

    public boolean equals(@Nullable Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof Foo)) {
            return false;
        }
        Foo foo = (Foo)other;
        if (this.int != foo.int) {
            return false;
        }
        return Intrinsics.areEqual((Object)this.s, (Object)foo.s);
    }
}

Kotlin generated a constructor for default value:

    public /* synthetic */ Foo(int n, String string, int n2, DefaultConstructorMarker defaultConstructorMarker) {
        if ((n2 & 1) != 0) {
            n = 10;
        }
        this(n, string);
    }

But it didn't provide this default value as a field, so we can't extract this value using reflection.

We could create a Foo object and check fields values:

  • If primitive fields value doesn't equal to java primitive value, then it's a default value
  • If object fields value doesn't equal to null, then it's a default value

And when deserializiing, use the computed default value to set such fields.

For codegen, wen need to extend MetaSharedCodecBuilder. When codegen disabled, MetaSharedSerializer needs to be extended

Describe alternatives you've considered

No response

Additional context

#1683

@chaokunyang
Copy link
Collaborator Author

Hi @wywen, are you interested in this issue?

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