jacob-carlborg/orange

Custom (des)serialization of arrays and strings fails.

jmcabo opened this issue · 2 comments

Custom deserialization of arrays and strings fails because arrays get written to the output many times. This happens because the array node gets reattached every time the serialize function is called by the custom serializer.
The code compiles fine, but there is an exception produced when deserializing:

    orange.serialization.SerializationException.SerializationException@orange\serialization\SerializationException.d(25): Could not unarchive the value with the key "z" due to malformed data.

The fix involves changing orange.xml.XmlDocument.Node.attach() from:

    void attach (Node node)
    {
        this.node ~= node.node;
    }

to:

    void attach (Node node)
    {
        //Note: Assigning and checking the parent fixes a problem
        //with duplicated nodes. (postProcessArray() called for every
        //serialize call).
        if (this.node !is node.node.parent) {
            node.node.parent = this.node;
            this.node ~= node.node;
        }
    }

Minimal example to reproduce the issue:

    import orange.serialization._;
    import orange.serialization.archives._;

    class MyCustom {
        int a;
        string z;
    }

    void myCustomToData(MyCustom mc, Serializer serializer, Serializer.Data key) {
        serializer.serialize(mc.a, "a");
        serializer.serialize(mc.z, "z");
    }

    void myCustomFromData (ref MyCustom mc, Serializer serializer, Serializer.Data key) {
        mc.a = serializer.deserialize!int("a");
        mc.z = serializer.deserialize!string("z");
    }

    void testMyCustom() {
        Serializer.registerSerializer!(MyCustom)(&myCustomToData);
        Serializer.registerDeserializer!(MyCustom)(&myCustomFromData);

        MyCustom mc = new MyCustom();   
        mc.a = 3;
        mc.z = "some string";

        XmlArchive!char archive = new XmlArchive!(char);
        Serializer serializer = new Serializer(archive);
        serializer.serialize(mc);

        //writeln(archive.data);  //<-- Here you can see that the string xml element is duplicated
                                  //This is because XmlArchive.postProcessArray() gets called
                                  //every time Serializer.serialize() is called. Thus duplicating
                                  //all the arrays, because Node.attach() doesn't check whether
                                  //it is reattaching the same node many times.

        //Deserialize. Fails with: "SerializationException: Could not unarchive
        //the value with the key "z" due to malformed data.":
        MyCustom after = serializer.deserialize!(MyCustom)(archive.untypedData);

        //Note: it also happens with int[]
    }

    void main() {
        testMyCustom();
    }

Tested with D 2.062

Thanks for the pull request.