#2693 Making better use of @Serializable it-blocks

SlimerDude Fri 27 Apr

I have a lot of classes that are instantiated by a container, and sometimes it is convenient to serialise them. Either to transport to / from a browser, or to use in complicated test cases.

Here is a simplified example:

@Serializable
const class Data {	
    @Transient
    const TransientData transData
    const Str           coreData
	
    new make(TransientData transData, |This| f) {
        this.transData = transData
        f(this)
    }
}

const class TransientData { }

To deserialise, Fantom can use the it-block to set the const fields, and any provided args to call the ctor:

dataFog := "cwPdf::Data { coreData=\"wotever\" }"

dataCls := (Data) dataFog.toBuf.readObj( ["makeArgs" : [TransientData()]] )

echo(dataCls.coreData)  // --> "wotever"

To make this work, the following needs to be applied:

diff -r 41d3fde11500 src/sys/java/fanx/serial/ObjDecoder.java
--- a/src/sys/java/fanx/serial/ObjDecoder.java	Thu Apr 26 09:16:05 2018 -0400
+++ b/src/sys/java/fanx/serial/ObjDecoder.java	Fri Apr 27 18:26:44 2018 +0100
@@ -232,12 +232,17 @@
     boolean setAfterCtor = true;
     try
     {
-      // if first parameter is an function then pass toSet
+      // if last parameter is an function then pass toSet
       // as an it-block for setting the fields
-      Param p = (Param)makeCtor.params().first();
-      if (args == null && p != null && p.type().fits(Sys.FuncType))
+      Param p = (Param)makeCtor.params().last();
+      if (p != null && p.type().fits(Sys.FuncType))
       {
-        args = new List(Sys.ObjType).add(Field.makeSetFunc(toSet));
+        if (args == null)
+          args = new List(Sys.ObjType);
+        else
+          // ensure args is a generic Obj list
+          args = new List(Sys.ObjType).addAll(args);
+        args.add(Field.makeSetFunc(toSet));
         setAfterCtor = false;
       }

brian Fri 27 Apr

I like the idea, but seems a little kludgy to me since really you could have a tree of objects. For example if it a list of objects, then what? It won't nest well.

Not sure I have a concrete alternative proposal, but couple ideas:

  • don't do anything and set it after the fact using AtomicRef
  • maybe make it more callback based on a per object basis

SlimerDude Sat 28 Apr

You could have a tree of objects, but inner objects tend to be the responsibility of their owner. It's the root object you're primariily converned with.

The current mechanism allows you to exclusively choose a list of ctor args or an it-block to set const fields. Given it is well documented that it-blocks must always appear at the end of the ctor paramter list, I'm simply saying, "Why not have both!?"

I'm not proposing a new feature, just an extension of what's there already. It doesn't preclude any future changes you might want to make.


For example if it a list of objects, then what?

I'm not sure I understand.

If your ctor has more params, you just pass more args to makeArgs as per usual:

@Serializable
const class Data {		
    new make(Obj arg1, Obj arg 2, |This| f) {
        f(this)
    }
}

buf.readObj( ["makeArgs" : [arg1, arg2]] )

brian Wed 2 May

Apologies - I had forgot how that worked in the root object case.

I wired up that change in both Java and JS versions and added a test suite. Let me know how that works for you

SlimerDude Thu 3 May

Brilliant! Thanks.

The Java version works well. Not tried it in JS yet, but I'm very happy it's been patched there too!

Login or Signup to reply.