使ORMLite对结构使用正确的序列化

时间:2013-11-27 15:30:22

标签: c# serialization servicestack ormlite-servicestack

TL; DR:

我正在结构上注册一个序列化器和一个反序列化器 未调用序列化程序,但解串器为。

我该如何解决这个问题? 它适用于引用类型,而JsConfig<Position>.TreatValueAsRefType = true;也没有帮助。


长版:

我使用ORMLite存储两个复杂类型:Position(一个结构,来自我们无法控制的外部库DotSpatial)和Tuple。

为了能够从数据库中正确存储/读取它们,我定义了它们的序列化器和反序列化器:

// Struct. Called by position.ToJsv(), NOT called by ORMLite's connection.Insert() .
JsConfig<Position>.SerializeFn = position =>
{
    string str = position.ToString(null, CultureInfo.InvariantCulture);
    return str; // Breakpoint here.
};
// Struct. Called in both.
JsConfig<Position>.DeSerializeFn = position => Position.Parse(position, CultureInfo.InvariantCulture);

// Reference type. Works fine.
JsConfig<Tuple<double, double>>.SerializeFn = tuple => string.Format(CultureInfo.InvariantCulture,
    "{0}{1}{2}",
    tuple.Item1, CultureInfo.InvariantCulture.TextInfo.ListSeparator, tuple.Item2
    );
// Works fine too.
JsConfig<Tuple<double, double>>.DeSerializeFn = tuple =>
{
    var values = tuple.Split(new[] { CultureInfo.InvariantCulture.TextInfo.ListSeparator }, StringSplitOptions.None);
    double item1, item2;
    if (values.Length == 2
        && double.TryParse(values[0], out item1)
        && double.TryParse(values[1], out item2))
    {
        var result = new Tuple<double, double>(item1, item2);
        return result;
    }
    throw new ArgumentException("Could not parse easting and northing from database; malformatted?", "tuple");
};

调试

使用ORMLite从数据库读取时,会发生反序列化程序中的断点:connection.Where<T>(item => item.Foo == bar)
使用ORMLite写入数据库时​​,序列化程序中的断点未被命中:connection.Insert(item)

我想也许串行器没有正确注册,所以我在对象上调用了.ToJsv()

var lat = Latitude.Parse("00°00'02.7451\"N", CultureInfo.InvariantCulture);
var lon = Longitude.Parse("013°29'17.3270\"W", CultureInfo.InvariantCulture);
Position pos = new Position(lat, lon);
string foo = pos.ToJsv(); // Works, hits the breakpoint.

点击断点时,str = 00°00'02.7451"N,013°29'17.3270"W
但是当使用ORMLite插入时,断点不会被触发,我在数据库中获取值,例如00°00'02,7451"N;013°29'17,3270"W - ,由于文化,请注意逗号。

数据库正在保存与文化相关的值! :(

的尝试

由于这只发生在结构上,我试图将要处理的类型注册为引用类型,但这似乎不起作用。

JsConfig<Position>.TreatValueAsRefType = true;

更新

我正在使用 ORMLite.PostgreSQL Nuget包(v 3.9.70)。它包括ServiceStack.Text(v 3.9.70)和Npgsql(v 2.0.11)。

我想尝试从源代码控制中获取代码并直接调试它,但是现在我没有时间。

Position结构在外部库中定义,我无法更改。

极简主义样本

我在https://gist.github.com/aneves/7830776上传了一个极简主义样本,其中显示了以下输出:

Thing, current culture: 12,6;10,9
Thing, invariant culture: 12.6,10.9
Thing, from Jsv: "12,6;10,9"
>> deserializing 10;35
>> Could not parse value, it is malformed. (10;35)
Found this: Box[A: 0;0]
Press any key to continue . . .

2 个答案:

答案 0 :(得分:3)

<强>更新

在GitHub上检查OrmLite的源代码后,它出现了:

    在将数据序列化到数据库中时,
  • JsConfig<Position>.TreatValueAsRefType从不进行检查。
  • 对于反序列化,OrmLite会始终调用TypeSerializer.DeserializeFromString,这就是您的方案仅适用于该方向的原因。

要解决此问题,我已将a patch提交到主存储库。同时,欢迎您使用此补丁重新编译OrmLite,或者只是使用重新编译的版本(基于4.0.3)我已经为您提供了here来代替NuGet中的相应文件之一

我希望此修复程序将合并到下一个正式版本中,也可以合并到3. *分支中。

原始答案:

如果您可以控制Position结构(似乎不是这种情况),您是否尝试重写ToString()?如果我没记错的话,OrmLite应该调用它:

struct Position {
    public override ToString(object o, CultureInfo culture) {
        /* Your serialization */
    }

    public override ToString() { // Will be used by OrmLite to serialize
        position.ToString(null, CultureInfo.InvariantCulture);
    }
}

它可能无法解决SerializeFn&lt;&gt;没有被调用,但可能对你的目标足够好,至少在错误修复之前。

答案 1 :(得分:2)

如果可能,我宁愿实施适当的解决方案。

现在我通过定义我自己的类MyPosition来修补问题,该类通过定义隐式运算符来模仿结构。
(忽略空值,它们是由于如何实现Position的ToString重载。)

JsConfig<MyPosition>.SerializeFn = position => position.ToString(null, CultureInfo.InvariantCulture);
JsConfig<MyPosition>.DeSerializeFn = position => Position.Parse(position, CultureInfo.InvariantCulture);

public class MyPosition
{
    public Latitude Latitude { get; set; }
    public Longitude Longitude { get; set; }

    public override string ToString()
    {
        return ToString(null, CultureInfo.CurrentCulture);
    }

    public string ToString(CultureInfo culture)
    {
        return ToString(null, culture);
    }

    public string ToString(string format, CultureInfo culture)
    {
        var pos = new Position(Latitude, Longitude);
        return pos.ToString(null, culture);
    }

    public static implicit operator MyPosition(Position toConvert)
    {
        return new MyPosition
        {
            Latitude = toConvert.Latitude,
            Longitude = toConvert.Longitude
        };
    }
    public static implicit operator Position(MyPosition toConvert)
    {
        return new Position(toConvert.Latitude, toConvert.Longitude);
    }
}