将一个未标记为可序列化的对象持久化存储

23

我需要将一个未标记为可序列化(serializable)属性的对象持久化。这个对象是来自于第三方库,无法进行更改。

我需要将它存储在一个持久化的地方,比如说文件系统,那么最优解决方案就是将对象序列化到一个文件中,但由于它没有被标记为可序列化,这并不是一个直接的解决方案。

这是一个非常复杂的对象,也包含了其他对象的集合。

你们有任何解决方法吗?代码永远不会在生产环境中运行,所以我可以接受几乎任何解决方案和性能。


12
著名的遗言……“这段代码永远不会在生产环境中运行” :oP - Matthew Whited
嘿,我知道 :) 这只是为了加快开发过程中的一些热身时间,因此在其他地方没有太多意义。 - lasseeskildsen
5个回答

9

XmlSerializer可能是一个有用的尝试,如果类型是公共的等等。

如果失败了,protobuf-net的v2版本(正在进行中,您需要从源代码构建,但我可以帮助)可以处理未经注释的对象,因此非常适合控制范围之外的类型,您只需要告诉它要包含什么(通过DSL)。 v2代码尚未完成,但它涵盖了大多数常见场景,包括集合等(未完成的工作主要是回调和枚举)。


protobuf-net看起来很不错。它需要无参构造函数吗? - lasseeskildsen
@lasseeskildsen - 目前是这样的,是的 - 但由于我拥有它,我肯定可以添加WCF方法(不调用任何构造函数)。 这只需要一点时间(只需调用FormatterServices.GetUninitializedObject)。 - Marc Gravell
如果你有第三方API的示例,我可能可以快速制作一个v2的示例。 - Marc Gravell
我对Commerce Server并不是非常熟悉,所以无法直接发表评论。 - Marc Gravell
@Marc:感谢你提供FormatterServices.GetUninitializedObject的技巧。我正在构建一组快速方法,但是无法想出如何创建没有无参构造函数的对象。这真是个巧妙的技巧...谢谢! - Matthew Whited
显示剩余2条评论

5
你可以编写一个递归方法,使用反射来遍历对象图以持久化对象... 但将其放回可能会更加困难。谁知道这些对象是否持有对未受管控或系统资源的引用。如果我要做这种事情,我会选择类型上的.GetFields(...)方法。 另一个想法... 如果你只是为了加快开发速度而这样做,为什么不使用自己的适配器类来包装他们的类。这将允许你用自己简化的模拟类替换第三方库,并为后续的替换和重用提供更好的机会。 虽然这很疯狂...但比我想象的要容易得多。(虽然这有效,但请考虑使用适配器来包装第三方类。)
public static class Tools
{
    public static XElement AsXml(this object input)
    {
        return input.AsXml(string.Empty);
    }
    public static XElement AsXml(this object input, string name)
    {
        if (string.IsNullOrEmpty(name))
            name = input.GetType().Name;

        var xname = XmlConvert.EncodeName(name);

        if (input == null)
            return new XElement(xname);

        if (input is string || input is int || input is float /* others */)
            return new XElement(xname, input);

        var type = input.GetType();
        var fields = type.GetFields(BindingFlags.Instance |
                                    BindingFlags.NonPublic)
                         .Union(type.GetFields(BindingFlags.Instance |
                                               BindingFlags.Public));

        var elems = fields.Select(f => f.GetValue(input)
                                        .AsXml(f.Name));

        return new XElement(xname, elems);
    }
    public static void ToObject(this XElement input, object result)
    {
        if (input == null || result == null)
            throw new ArgumentNullException();

        var type = result.GetType();
        var fields = type.GetFields(BindingFlags.Instance |
                                    BindingFlags.NonPublic)
                         .Union(type.GetFields(BindingFlags.Instance |
                                               BindingFlags.Public));

        var values = from elm in input.Elements()
                     let name = XmlConvert.DecodeName(elm.Name.LocalName)
                     join field in fields on name equals field.Name
                     let backType = field.FieldType
                     let val = elm.Value
                     let parsed = backType.AsValue(val, elm)
                     select new
                     {
                         field,
                         parsed
                     };

        foreach (var item in values)
            item.field.SetValue(result, item.parsed);            
    }

    public static object AsValue(this Type backType,
                                      string val,
                                      XElement elm)
    {
        if (backType == typeof(string))
            return (object)val;
        if (backType == typeof(int))
            return (object)int.Parse(val);
        if (backType == typeof(float))
            return (float)int.Parse(val);

        object ret = FormatterServices.GetUninitializedObject(backType);
        elm.ToObject(ret);
        return ret;
    }
}
public class Program
{
    public static void Main(string[] args)
    {
        var obj = new { Matt = "hi", Other = new { ID = 1 } };
        var other = new { Matt = "zzz", Other = new { ID = 5 } };
        var ret = obj.AsXml();
        ret.ToObject(other);
        Console.WriteLine(obj); //{ Matt = hi, Other = { ID = 1 } }
        Console.WriteLine(other); //{ Matt = hi, Other = { ID = 1 } }
    }
}

是的,我刚试着构建了一个快速解决方案来持久化和恢复基于私有值的对象。持久化相当容易;问题在于恢复它们。如果您没有访问无参数构造函数,则可能是不可能的。 - Matthew Whited
如果上述代码导致世界爆炸并导致宇宙中所有生命突然终结,我不承担任何责任。 - Matthew Whited
目前仅支持字符串、整数和浮点数。您可能需要添加其他本地类型,或者至少更改类型选择代码的工作方式。享受,玩得开心……但请不要在家里尝试 ;) - Matthew Whited
由于循环对象序列化,这可能会导致堆栈溢出漏洞。 - Cogent

3

链接已经失效。第一篇文章:链接 第二篇文章:链接 - milosa

2

我不知道这对你的使用是否过于复杂,但我最近一直在尝试使用db4o。它可以持久化任何对象,只需调用IObjectContainer.Store(object)即可,而且它非常轻量级且基于文件。不需要安装。

目前为止,我还没有遇到任何问题。


0
///Here OBJECT is Class name and Object_to_write is instance  
XmlSerializer serializer = new XmlSerializer(typeof(OBJECT)); 
using (TextWriter writer = new StreamWriter(@"C:\Xml.xml"))
{
    serializer.Serialize(writer, OBJECT_to_Write); 
}

该对象来自于一个我无法更改的第三方库。XmlSerializer 可能会抛出 InvalidOprationException 异常:[该对象] 无法序列化,因为它没有无参构造函数。 - Alex 75

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接