fury icon indicating copy to clipboard operation
fury copied to clipboard

[Kotlin] Kotlin default value support in CompatibleMode

Open chaokunyang opened this issue 1 year ago • 1 comments

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

https://github.com/apache/fury/issues/1683

chaokunyang avatar Dec 04 '24 04:12 chaokunyang

Hi @wywen, are you interested in this issue?

chaokunyang avatar Dec 04 '24 05:12 chaokunyang