August 23, 2013
On Friday, 23 August 2013 at 13:39:47 UTC, Dicebot wrote:
> On Friday, 23 August 2013 at 13:34:04 UTC, ilya-stromberg wrote:
>> It's a serious issue. May be it's more important than range support. For example, I have to change class (bug fixing, new features, etc.), but it comparable with previos version (example: it's always possible to convert "int" to "long"). I that case I can't use std.serialization and have to write own solution (for examle, save data in csv file).
>
> I don't think it as an issue at all. Behavior you want can't be defined in a generic way, at least not without lot of UDA help or similar declarative approach. In other words, the fact that those two classes are interchangeable in the context of the serialization exists only in the mind of programmer, not in D type system.
>
> More than that, such behavior goes seriously out of the line of D being strongly typed language. I think functionality you want does belong to a more specialized module, not generic std.serialization - maybe even format-specific.

What about adding delegate hooks in somewhere? These delegates would be called on errors like invalid type or missing field.

I'm not saying this needs to be there in order to release, but would this be a direction we'd like to go eventually? I've seen similar approaches elsewhere (e.g. Node.js's HTTP parser).
August 23, 2013
On 2013-08-22 21:30, ilya-stromberg wrote:

> Great! What about more difficult cases? For example, we have:
>
> class Foo
> {
>     int a;
>     int b;
> }
>
> After changes we have new class:
>
> class Foo
> {
>     long b;
> }
>
> Can std.serialization load data to new class from old file? It should
> ignore "a" and convert "b" from int to long.

Actually, my previous answer was not entirely correct. By default it will throw an exception. But you can implement the above using custom serialization (here using Orange) :

module main;

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

import std.stdio;

class Foo : Serializable
{
    long b;

    void toData (Serializer serializer, Serializer.Data key)
    {
    }

    void fromData (Serializer serializer, Serializer.Data key)
    {
        b = serializer.deserialize!(int)("b");
    }
}

void main ()
{
    auto archive = new XmlArchive!(char);
    auto serializer = new Serializer(archive);

    auto data = `<?xml version="1.0" encoding="UTF-8"?>
    <archive version="1.0.0" type="org.dsource.orange.xml">
        <data>
            <object runtimeType="main.Foo" type="main.Foo" key="0" id="0">
                <int key="a" id="1">3</int>
                <int key="b" id="2">4</int>
            </object>
        </data>
    </archive>`;

    auto f = serializer.deserialize!(Foo)(cast(immutable(void)[]) data);
    assert(f.b == 4);
}

-- 
/Jacob Carlborg
August 23, 2013
On 2013-08-23 16:39, Tyler Jameson Little wrote:

> What about adding delegate hooks in somewhere? These delegates would be
> called on errors like invalid type or missing field.
>
> I'm not saying this needs to be there in order to release, but would
> this be a direction we'd like to go eventually? I've seen similar
> approaches elsewhere (e.g. Node.js's HTTP parser).

std.serialization already supports delegate hooks for missing values:

https://dl.dropboxusercontent.com/u/18386187/docs/std.serialization/std_serialization_serializer.html#.Serializer.errorCallback

-- 
/Jacob Carlborg
August 24, 2013
On Friday, 23 August 2013 at 20:29:40 UTC, Jacob Carlborg wrote:
> On 2013-08-23 16:39, Tyler Jameson Little wrote:
>
>> What about adding delegate hooks in somewhere? These delegates would be
>> called on errors like invalid type or missing field.
>>
>> I'm not saying this needs to be there in order to release, but would
>> this be a direction we'd like to go eventually? I've seen similar
>> approaches elsewhere (e.g. Node.js's HTTP parser).
>
> std.serialization already supports delegate hooks for missing values:
>
> https://dl.dropboxusercontent.com/u/18386187/docs/std.serialization/std_serialization_serializer.html#.Serializer.errorCallback

Awesome!
August 24, 2013
On Friday, 23 August 2013 at 20:28:10 UTC, Jacob Carlborg wrote:
> On 2013-08-22 21:30, ilya-stromberg wrote:
>
>> What about more difficult cases?
>
> Actually, my previous answer was not entirely correct. By default it will throw an exception. But you can implement the above using custom serialization (here using Orange) :

Great job!
A little question. For example, I would like to load data from previos format and store current version in default std.serialization format. So, I don't want to implement "toData" at all? Is it possible? Or can I call the default serialization method? Something like this:

class Foo : Serializable
{
    long b;

    //I don't want to implement this
    void toData (Serializer serializer, Serializer.Data key)
    {
        serializer.serialize(this);
    }

    void fromData (Serializer serializer, Serializer.Data key)
    {
        b = serializer.deserialize!(int)("b");
    }
}

Also, please add this examlpe to the documentation, it could be useful for many users.

Note that we can split Serializable interface for 2 interfaces:

interface ToSerializable
{
    void toData(Serializer serializer, Serializer.Data key);
}

interface FromSerializable
{
    void fromData(Serializer serializer, Serializer.Data key);
}

interface Serializable : ToSerializable, FromSerializable
{
}

class Foo : FromSerializable
{
    long b;

    void fromData (Serializer serializer, Serializer.Data key)
    {
        b = serializer.deserialize!(int)("b");
    }

    //I must NOT to implement toData
}
August 24, 2013
On 2013-08-24 14:45, ilya-stromberg wrote:

> Great job!
> A little question. For example, I would like to load data from previos
> format and store current version in default std.serialization format.
> So, I don't want to implement "toData" at all? Is it possible? Or can I
> call the default serialization method? Something like this:
>
> class Foo : Serializable
> {
>      long b;
>
>      //I don't want to implement this
>      void toData (Serializer serializer, Serializer.Data key)
>      {
>          serializer.serialize(this);
>      }
>
>      void fromData (Serializer serializer, Serializer.Data key)
>      {
>          b = serializer.deserialize!(int)("b");
>      }
> }

I actually noticed this problem when I wrote the example. First, the interface Serializable is actually not necessary because this is actually checked with at template at compile time, it's possible to use these methods for structs as well. Second, instead of checking for both "toData" and "fromData" when serializing and deserializing it should only check for "toData" when serializing and only for "fromData" when deserializing.

I'll add this to my todo list.

-- 
/Jacob Carlborg
August 24, 2013
On Saturday, 24 August 2013 at 17:47:35 UTC, Jacob Carlborg wrote:
> I actually noticed this problem when I wrote the example. First, the interface Serializable is actually not necessary because this is actually checked with at template at compile time, it's possible to use these methods for structs as well. Second, instead of checking for both "toData" and "fromData" when serializing and deserializing it should only check for "toData" when serializing and only for "fromData" when deserializing.

In that case maybe we should remove "Serializable" interface? And just spesify that user must implement "toData" or "fromData" for custom serializing or deserializing. Is it possible?
August 24, 2013
On 2013-08-24 21:26, ilya-stromberg wrote:

> In that case maybe we should remove "Serializable" interface? And just
> spesify that user must implement "toData" or "fromData" for custom
> serializing or deserializing. Is it possible?

Yes, that's what I'm planning to do.

-- 
/Jacob Carlborg
August 24, 2013
On Saturday, 24 August 2013 at 19:32:13 UTC, Jacob Carlborg wrote:
> On 2013-08-24 21:26, ilya-stromberg wrote:
>
>> In that case maybe we should remove "Serializable" interface? And just
>> spesify that user must implement "toData" or "fromData" for custom
>> serializing or deserializing. Is it possible?
>
> Yes, that's what I'm planning to do.

Maybe we should rename methods "toData" and "fromData" to avoid name collisions? For example, we can use serializeToData and deserializeFromData, it will be clearer.
August 26, 2013
On Saturday, 24 August 2013 at 17:47:35 UTC, Jacob Carlborg wrote:
> First, the interface Serializable is actually not necessary because this is actually checked with at template at compile time, it's possible to use these methods for structs as well. Second, instead of checking for both "toData" and "fromData" when serializing and deserializing it should only check for "toData" when serializing and only for "fromData" when deserializing.

The name "isSerializable" is TERRIBLE:
https://dl.dropboxusercontent.com/u/18386187/docs/std.serialization/std_serialization_serializable.html#.isSerializable
It only checks if functions "toData" and "fromData" exists in a class or struct. But std.serialization can serialize almost any data, so please rename the template, like "hasCustomSerialization".

The real "isSerializable" must check if it's possible to serialize and should look like this:
enum isSerializable(T) = serializer.serialize(T);