How can I customize the serialization/deserialization of a complex type in ServiceStack.OrmLite
Asked Answered
F

3

7

I am using ServiceStack.OrmLite to persist data in a SQLite database, and quite happy with it so far.

However, many of my objects have properties of a complex type which I don't want serialized using JSV.

I need to be able to specify a custom serialization/deserialization that should be used to store the property in the database (as a string). In the db4o world this would be equivalent to using the translation feature provided by IObjectConstructor.

A good example of complex types that cannot be serialized and deserialized properly are NodaTime types, even though they can be easily mapped to strings (I already have the serialization/deserialization functions which I used with db4o).

What is the simplest way to achieve that? A wrapper would not be very convenient as I would have to write and maintain one for each type that contains a property of this complex type.

Frostbite answered 1/11, 2013 at 2:4 Comment(2)
Please, give a minimum working example of a class, how would you like to serealize it and, how to deserelize it.Nephralgia
It is very simple, I have a basic class with a property that is of a complex type. I need this property to be serialized/deserialized into/from a string using my own routine rather than JSV. I don't think a code sample is really needed in this specific instance.Frostbite
F
6

For those who may be interested in the wrapper pattern I am currently using to implement custom serialization with OrmLite (works with other ORMs too), here is a simple working example with NodaTime types that are otherwise not serialized properly:

public class BusinessObject {
    public class Poco {
        public readonly BusinessObject BusinessObject;

        public Poco(BusinessObject businessObject) {
            this.BusinessObject = businessObject;
        }

        public Poco() {
            this.BusinessObject = new BusinessObject();
        }

        public string Id {
            get { return this.BusinessObject.Id; }
            set { this.BusinessObject.Id = value; }
        }

        public decimal Amount {
            get { return this.BusinessObject.Amount; }
            set { this.BusinessObject.Amount = value; }
        }

        public DateTime Dt {
            get { return this.BusinessObject.Dt.ToDateTime(); }
            set { this.BusinessObject.Dt = LocalDateTime.FromDateTime(value).Date; }
        }

        public string TimeZone {
            get { return this.BusinessObject.TimeZone.Id; }
            set { this.BusinessObject.TimeZone = DateTimeZoneProviders.Tzdb.GetZoneOrNull(value); }
        }

        public string Description {
            get { return this.BusinessObject.Description; }
            set { this.BusinessObject.Description = value; }
        }
    }

    public string Id { get; private set; }
    public decimal Amount { get; private set; }
    public LocalDate Dt { get; private set; }
    public DateTimeZone TimeZone { get; private set; }
    public string Description { get; private set; }

    public BusinessObject() { }

    public BusinessObject(string id, decimal amount, LocalDate dt, DateTimeZone timeZone, string description) {
        this.Id = id;
        this.Amount = amount;
        this.Dt = dt;
        this.TimeZone = timeZone;
        this.Description = description;
    }
}

I hope it will soon be possible to define hooks/callbacks for specific types that should be handled with custom code, and also that OrmLite will allow properties with private setters to be reloaded from persistence (currently it will only work in one direction).

Update (2014/03/08): As a temporary workaround, it is possible to have OrmLite use custom serialization/deserialization by calling first:

JsConfig<BusinessObject>.TreatValueAsRefType = true;

Even if BusinessObject is a reference type. Then, you can enjoy the beauty/simplicity/ubiquity of:

JsConfig<BusinessObject>.RawSerializeFn = bo => bo.Serialize();
JsConfig<BusinessObject>.RawDeserializeFn = str => BusinessObject.Deserialize(str);

Hopefully support for custom mapping will be added soon (so that for example a NodaTime.LocalDate can be mapped to a DateTime instead of a string).

Frostbite answered 2/11, 2013 at 13:30 Comment(2)
I really don't see how private setters could be loaded externally, or if they should at all. protected could work, though.Mississippian
Private setters are accessible with Reflection.Frostbite
C
2

OrmLite now supports pluggable text serializers.

Pluggable serialization lets you specify different serialization strategies of Complex Types for each available RDBMS provider, e.g:

//ServiceStack's JSON and JSV Format
SqliteDialect.Provider.StringSerializer = new JsvStringSerializer();       
PostgreSqlDialect.Provider.StringSerializer = new JsonStringSerializer();
//.NET's XML and JSON DataContract serializers
SqlServerDialect.Provider.StringSerializer = new DataContractSerializer();
MySqlDialect.Provider.StringSerializer = new JsonDataContractSerializer();
//.NET XmlSerializer
OracleDialect.Provider.StringSerializer = new XmlSerializableSerializer();

You can also provide a custom serialization strategy by implementing IStringSerializer:

public interface IStringSerializer
{
    To DeserializeFromString<To>(string serializedText);
    object DeserializeFromString(string serializedText, Type type);
    string SerializeToString<TFrom>(TFrom from);
}
Columbium answered 1/11, 2013 at 6:19 Comment(0)
M
2

To serialize complex types, set up your own serializer (and deserializer) on JsConfig:

JsConfig<Foo>.SerializeFn = foo => foo.ToString("XOXO", CultureInfo.InvariantCulture);
JsConfig<Foo>.DeSerializeFn = foo =>
{
    var result = Foo.Parse(foo, CultureInfo.InvariantCulture);
    return result;
};

You might also want to tell JsConfig to assume UTC dates:

JsConfig.Init(new Config {
    JsConfig.AssumeUtc = true
});
Mississippian answered 27/11, 2013 at 12:37 Comment(2)
Does this work with OrmLite? I know it works with ServiceStack, but as far as I had read and tested with my code it was not taken into account for OrmLite serialization.Frostbite
It does. But I am having trouble with the serialization of structs. (Deserialization works fine. And for reference types both work fine - serialization and deserialization.)Mississippian

© 2022 - 2024 — McMap. All rights reserved.