持久化未标记为可序列化的对象

Posted

技术标签:

【中文标题】持久化未标记为可序列化的对象【英文标题】:Persist an object that is not marked as serializable 【发布时间】:2011-02-05 10:39:36 【问题描述】:

我需要保留一个未标记可序列化属性的对象。该对象来自我无法更改的第 3 方库。

我需要将它存储在持久性位置,例如文件系统,因此最佳解决方案是将对象序列化为文件,但由于它没有标记为可序列化,这不是一个直截了当解决方案。

这是一个相当复杂的对象,它还包含其他对象的集合。

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

【问题讨论】:

著名的遗言... The code will never run in a production environment :oP 嘿,我知道 :) 这只是为了加快开发过程中的一些热身时间,所以在其他任何地方都没有多大意义。 【参考方案1】:

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

如果失败,protobuf-net 的 v2(正在进行中,您需要从源代码构建,但我可以提供帮助)适用于未归属的对象,非常适合您无法控制的类型 - 您只需要告诉它什么包括(通过 DSL)。 v2 代码不完整,但涵盖了最常见的场景,包括集合等(不完整的工作主要是回调和枚举)。

【讨论】:

protobuf-net 看起来不错。是否需要无参数构造函数? @lasseeskildsen - 目前确实如此 - 但既然我拥有它,我确信我可以添加 WCF 方法(不调用任何 ctor)。只需片刻(只需致电FormatterServices.GetUninitializedObject)。 如果您有第 3 方 API 的示例,我可能会创建一个 v2 示例。 不幸的是,第 3 方 API 是 MS Commerce Server。它使用 HTTP 模块在第一次请求时将大量内容缓存到静态变量中,因此我创建了另一个模块,该模块使用反射来获取该字段,然后再次设置它。所以我只需要在应用重新启动时在某个地方存储值。 我对 Commerce Server 不是很熟悉,因此无法直接对此发表评论。【参考方案2】:

您可以编写一个递归方法,该方法将使用反射沿对象图运行以保持对象...将其放回原处可能要困难得多。谁知道这些对象中是否有任何对象持有对非托管或系统资源的引用。如果我要做这种疯狂的事情,我会选择类型上的.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  
    

【讨论】:

是的,我只是尝试构建一个快速解决方案来基于私有值持久化和恢复对象。坚持很容易;问题在于恢复它们。如果您无权访问无参数构造函数,那可能是不可能的。 如果上述代码导致世界爆炸,宇宙中所有生命戛然而止,我概不负责 酷 - 我会看看它。谢谢:-) 目前仅支持字符串、整数和浮点数。您可能需要添加其他原生类型,或者至少更改类型选择代码的工作方式。享受,玩得开心......请不要在家里尝试这个;) 由于循环对象序列化,这很容易受到堆栈溢出的影响。【参考方案3】:

这是您可以做到的一种方式:

http://www.codeproject.com/KB/dotnet/Surrogate_Serialization.aspx

这是显示它的 msdn 链接:

http://msdn.microsoft.com/en-us/magazine/cc188950.aspx

【讨论】:

链接失效了。第一篇:link 第二篇:link【参考方案4】:

我不知道这对你的使用是否过分,但我最近一直在玩db4o。它将持久化任何对象,只需调用 IObjectContainer.Store(object),它是轻量级和基于文件的。不需要任何安装。

我还没有遇到任何问题。

【讨论】:

【参考方案5】:
///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); 

【讨论】:

> 该对象来自我无法更改的第 3 方库。 XmlSerializer 可能会抛出 InvalidOprationException:[对象] 无法序列化,因为它没有无参数构造函数。

以上是关于持久化未标记为可序列化的对象的主要内容,如果未能解决你的问题,请参考以下文章

创建 WCF 代理时数据协定未标记为可序列化

AppDomain CreateInstanceAndUnwrap:类型未标记为可序列化

UserControl 集合未标记为可序列化

类型“System.Web.HttpInputStream”未标记为可序列化

创建与插件一起使用的应用程序域:“程序集中的类型未标记为可序列化

如何识别c__DisplayClass未标记为可序列化的位置