question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[core-serialiser] Is serialisation of Optional values supported?

See original GitHub issue

Is serialisation of Optional values supported?

I can see some references to supporting optional values in the codebase – such as BinarySerializers.ofOptional – but a naive attempt to serialise an optional value fails with:

Exception in thread "main" java.lang.IllegalArgumentException: Method not found: <init> []
	at io.activej.codegen.Context.findMethod(Context.java:387)
	at io.activej.codegen.Context.invokeConstructor(Context.java:324)
	at io.activej.codegen.Context.invokeConstructor(Context.java:318)
	at io.activej.codegen.expression.ExpressionConstructor.load(ExpressionConstructor.java:40)
	at io.activej.codegen.expression.ExpressionLet.load(ExpressionLet.java:34)
	at io.activej.codegen.expression.ExpressionSequence.load(ExpressionSequence.java:44)
	at io.activej.codegen.expression.ExpressionLet.load(ExpressionLet.java:34)
	at io.activej.codegen.expression.ExpressionSequence.load(ExpressionSequence.java:44)
	at io.activej.codegen.ClassBuilder.defineNewClass(ClassBuilder.java:380)
	at io.activej.codegen.ClassBuilder.build(ClassBuilder.java:293)
	at io.activej.codegen.ClassBuilder.buildClassAndCreateNewInstance(ClassBuilder.java:447)
	at io.activej.serializer.SerializerBuilder.buildImpl(SerializerBuilder.java:854)
	at io.activej.serializer.SerializerBuilder.build(SerializerBuilder.java:316)
	at io.activej.serializer.SerializerBuilder.build(SerializerBuilder.java:310)

This uses:

  public static class OptionalTest
  {
    @Serialize(order = 0)
    public final Optional<String> optional;

    public OptionalTest(@Deserialize("optional") final Optional<String> optional)
    {
      this.optional = optional;
    }
  }

and

    final OptionalTest optionalTest = new OptionalTest(Optional.of("hello, world"));
    final BinarySerializer<OptionalTest> serializer =
        SerializerBuilder.create()
            .build(OptionalTest.class);

    final StreamOutput stream = StreamOutput.create(new ByteArrayOutputStream());
    serializer.encode(stream.getBinaryOutput(), optionalTest);

How do we serialise Optional values?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
eduard-vasinskyicommented, Mar 9, 2021

@gkopff I forgot to add a casting to Foo in SerializerDefOptional#encoder. Here is an updated version of SerializerDefOptional. Now, Optionals are serialized/deserialized correctly.

0reactions
gkopffcommented, Mar 9, 2021

Here’s the whole thing (it also needs your SerializerDefOptional).

import io.activej.codegen.expression.Expression;
import io.activej.codegen.expression.Variable;
import io.activej.serializer.AbstractSerializerDef;
import io.activej.serializer.BinarySerializer;
import io.activej.serializer.CompatibilityLevel;
import io.activej.serializer.SerializerBuilder;
import io.activej.serializer.SerializerDef;
import io.activej.serializer.annotations.Deserialize;
import io.activej.serializer.annotations.Serialize;
import io.activej.serializer.util.BinaryOutputUtils;

import java.util.Optional;

import static io.activej.codegen.expression.Expressions.call;
import static io.activej.codegen.expression.Expressions.set;
import static io.activej.codegen.expression.Expressions.staticCall;
import static io.activej.serializer.util.Utils.of;

public class OptionalBugApp
{
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  public static void main(String[] args)
//  {
//    final BinarySerializer<FooHolder> serializer =
//        SerializerBuilder.create()
//            .withSerializer(Foo.class, new SerializerDefFoo())
//            .withSerializer(Optional.class, (type, generics, target) -> new SerializerDefOptional(generics[0].serializer))
//            .build(FooHolder.class);
//
//    final byte[] array = new byte[16];
//    final int length = serializer.encode(array, 0, new FooHolder(new Foo("bar")));
//    System.out.println("Wrote: " + length + " bytes");
//
//    final FooHolder decoded = serializer.decode(array, 0);
//    System.out.println("Finished: " + decoded);
//  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  public static void main(String[] args)
//  {
//    final BinarySerializer<OptionalStringHolder> serializer =
//        SerializerBuilder.create()
//            .withSerializer(Foo.class, new SerializerDefFoo())
//            .withSerializer(Optional.class, (type, generics, target) -> new SerializerDefOptional(generics[0].serializer))
//            .build(OptionalStringHolder.class);
//
//    final byte[] array = new byte[16];
//    final int length = serializer.encode(array, 0, new OptionalStringHolder(Optional.of("bar")));
//    System.out.println("Wrote: " + length + " bytes");
//
//    final OptionalStringHolder decoded = serializer.decode(array, 0);
//    System.out.println("Finished: " + decoded);
//  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  public static void main(String[] args)
  {
    final BinarySerializer<OptionalFooHolder> serializer =
        SerializerBuilder.create()
            .withSerializer(Foo.class, new SerializerDefFoo())
            .withSerializer(Optional.class, (type, generics, target) -> new SerializerDefOptional(generics[0].serializer))
            .build(OptionalFooHolder.class);

    final byte[] array = new byte[16];
    final int length = serializer.encode(array, 0, new OptionalFooHolder(Optional.of(new Foo("bar"))));
    System.out.println("Wrote: " + length + " bytes");

    final OptionalFooHolder decoded = serializer.decode(array, 0);
    System.out.println("Finished: " + decoded);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  public static class FooHolder
  {
    @Serialize(order = 0)
    public final Foo foo;

    public FooHolder(@Deserialize("foo") final Foo foo)
    {
      this.foo = foo;
    }

    @Override
    public String toString() { return "FooHolder{" + "foo=" + this.foo + '}'; }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  public static class OptionalStringHolder
  {
    @Serialize(order = 0)
    public final Optional<String> optionalString;

    public OptionalStringHolder(@Deserialize("optionalString") final Optional<String> optionalString)
    {
      this.optionalString = optionalString;
    }

    @Override
    public String toString() { return "OptionalStringHolder{" + "foo=" + this.optionalString + '}'; }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  public static class OptionalFooHolder
  {
    @Serialize(order = 0)
    public final Optional<Foo> optionalFoo;

    public OptionalFooHolder(@Deserialize("optionalFoo") final Optional<Foo> optionalFoo)
    {
      this.optionalFoo = optionalFoo;
    }

    @Override
    public String toString() { return "OptionalFooHolder{" + "optionalFoo=" + this.optionalFoo + '}'; }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  public static class Foo
  {
    private final String s;

    private Foo(final String s)
    {
      this.s = s;
    }

    public static Foo make(final String s)
    {
      return new Foo(s);
    }

    public String getString()
    {
      return this.s;
    }

    @Override
    public String toString() { return "Foo{" + "s='" + this.s + '\'' + '}'; }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  public static class SerializerDefFoo extends AbstractSerializerDef implements SerializerDef
  {
    @Override
    public Class<?> getEncodeType()
    {
      return Foo.class;
    }

    @Override
    public Expression encoder(final StaticEncoders staticEncoders,
                              final Expression buf,
                              final Variable pos,
                              final Expression value,
                              final int version,
                              final CompatibilityLevel compatibilityLevel)
    {
      return set(
          pos,
          of(() -> staticCall(BinaryOutputUtils.class, "writeUTF8", buf, pos, call(value, "getString"))));
    }

    @Override
    public Expression decoder(final StaticDecoders staticDecoders,
                              final Expression in,
                              final int version,
                              final CompatibilityLevel compatibilityLevel)
    {
      return staticCall(Foo.class, "make", call(in, "readUTF8"));
    }
  }
}

The two commented out main methods work.

The first serialises a FooHolder which contains a Foo. This works and utilises a SerializerDefFoo to serialise the Foo that’s inside the FooHolder.

The second serialises a OptionalStringHolder which contains an Optional<String>. This works and utilises your SerializerDefOptional to serialise the Optional<String> that’s inside the OptionalStringHolder.

Finally, the third one – the one not commented out – tries to serialise a OptionalFooHolder which contains a Optional<Foo>. However, this fails with:

Exception in thread "main" java.lang.IllegalArgumentException: Method not found: getString []
	at io.activej.codegen.Context.findMethod(Context.java:387)
	at io.activej.codegen.Context.invoke(Context.java:258)
	at io.activej.codegen.Context.invoke(Context.java:244)
	at io.activej.codegen.Context.invoke(Context.java:234)
	at io.activej.codegen.expression.ExpressionCall.load(ExpressionCall.java:38)
	at io.activej.codegen.Context.invokeStatic(Context.java:281)
	at io.activej.codegen.expression.ExpressionStaticCall.load(ExpressionStaticCall.java:40)
	at io.activej.codegen.expression.ExpressionSet.load(ExpressionSet.java:37)
	at io.activej.codegen.expression.ExpressionSequence.load(ExpressionSequence.java:44)
	at io.activej.codegen.expression.ExpressionIf.load(ExpressionIf.java:52)
	at io.activej.codegen.expression.ExpressionSequence.load(ExpressionSequence.java:44)
	at io.activej.codegen.expression.ExpressionSequence.load(ExpressionSequence.java:44)
	at io.activej.codegen.expression.ExpressionSequence.load(ExpressionSequence.java:44)
	at io.activej.codegen.ClassBuilder.defineNewClass(ClassBuilder.java:380)
	at io.activej.codegen.ClassBuilder.build(ClassBuilder.java:293)
	at io.activej.codegen.ClassBuilder.buildClassAndCreateNewInstance(ClassBuilder.java:447)
	at io.activej.serializer.SerializerBuilder.buildImpl(SerializerBuilder.java:854)
	at io.activej.serializer.SerializerBuilder.build(SerializerBuilder.java:316)
	at io.activej.serializer.SerializerBuilder.build(SerializerBuilder.java:310)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Serialize/De-Serialize Optional values using IoBuffer
In our project we are using the org.apache.mina.core.buffer.IoBuffer to serialize and deserialize objects and send it through the network.
Read more >
Default serialization format for TimeSpan - .NET
This support changes the default serialization format for TimeSpan values in source generators. Previous behavior. In .NET 6 GA, source ...
Read more >
Serialization in Java - DigitalOcean
Serialization in Java allows us to convert an Object to stream that ... Notice that the method arguments work with Object that is...
Read more >
Using Optional with Jackson - Baeldung
If we think about it, what we want is for actual the value of the subtitle field to be serialized. 2.4. Deserialization.
Read more >
Java Object Serialization Specification: 1 - System Architecture
Objects to be saved in the stream may support either the Serializable or the ... are saved or restored, and write and read...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found