Serialization

Sric supports serialization through built-in dynamic reflection, using the HiML text format.

Serialization Example

Classes to be serialized require the reflect marker:

reflect struct Point {
    var x: Int;
    var y: Float;
}

unsafe fun testSimple() {
    var encoder: Encoder;
    var obj = new Point { .x = 1; .y = 2; };
    var t = obj as *Void;
    var res = encoder.encode(t, "testSerial::Point");
    printf("%s\n", res.c_str());

    var decoder: Decoder;
    var p = decoder.decode(res);
    var obj2: raw* Point = unsafeCast$<raw*Point>(p);
    
    verify(obj2.x == obj.x);
    verify(obj2.y == obj.y);
}

For non-polymorphic objects like Point, the type name "testSerial::Point" must be explicitly provided.

Skipping Serialization

Use Transient annotation to exclude fields:

reflect struct Point {
    var x: Int;
    //@Transient
    var y: Float;
}

Post-Deserialization Handling

The _onDeserialize method is automatically called after deserialization:

reflect struct Point {
    var x: Int;
    fun _onDeserialize() {
    }
}

Simple Serialization Mode

Normally custom classes serialize as HiML objects. The SimpleSerial annotation enables string serialization (e.g. "1 2 3 4" instead of structured formatInsets{top=1,right=2,bottom=3,left=4}):

//@SimpleSerial
reflect struct Insets {
    var top: Int = 0;
    var right: Int = 0;
    var bottom: Int = 0;
    var left: Int = 0;

    fun toString() : String {
        return String::format("%d %d %d %d", top, right, bottom, left);
    }

    fun fromString(str: String): Bool {
        var fs = str.split(" ");
        if (fs.size() == 4) {
            top = fs[0].toInt32();
            right = fs[1].toInt32();
            bottom = fs[2].toInt32();
            left = fs[3].toInt32();
            return true;
        }
        return false;
    }
}

Serialization/deserialization automatically calls toString and fromString - these methods must exactly match the shown signatures.