Persist an object that is not marked as serializable
Asked Answered
J

5

23

I need to persist an object that is not marked with the serializable attribute. The object is from a 3rd party library which I cannot change.

I need to store it in a persist place, like for example the file system, so the optimal solution would be to serialize the object to a file, but since it isn't marked as serializable, that is not a straight forward solution.

It's a pretty complex object, which also holds a collection of other objects.

Do you guys have any input on how to solve this? The code will never run in a production environment, so I'm ok with almost any solution and performance.

Jaquenette answered 7/4, 2010 at 20:28 Comment(2)
Famous last words... The code will never run in a production environment :oPMusketry
Heh, I know :) It's only to speed up some warm up time during dev, so it doesn't make much sense anywhere else.Jaquenette
T
9

XmlSerializer may be a useful first thing to try, if the types are public etc

If that fails, v2 of protobuf-net (in progress, you'd need to build from source, but I can help) works with unattributed objects, so ideal for types outside your control - you just need to tell it what to include (via a DSL). The v2 code isn't complete, but it covers most common scenarios, including collections etc (the incomplete work is mainly callbacks and enums).

Toile answered 7/4, 2010 at 20:33 Comment(7)
protobuf-net looks pretty nice. Does it require parameterless constructors?Jaquenette
@Jaquenette - at the moment it does, yes - but since I own it I'm sure I could add the WCF approach (of not calling any ctor). It would take only moments (it is just a call to FormatterServices.GetUninitializedObject).Toile
If you have an example of the 3rd-party API I could proabably whip up a v2 example.Toile
Unfortunately, the 3rd party API is MS Commerce Server. It uses a HTTP module to cache a lot of stuff into a static variable at the first request, so I have created another module, that uses reflection to grab that field, and set it again. So I only need somewhere to store the value when the app restarts.Jaquenette
I'm not hugely familiar with Commerce Server, so I can't comment directly on that.Toile
@Marc: thanks for the tip on FormatterServices.GetUninitializedObject I was working on building a quick set of methods for this and couldn't figure out how to create an object without the parameterless constructor. That's a slick trick... THANKS!Musketry
by the way, I can confirm that v2 is capable of skipping constructors (opt-in)Toile
M
5

You could write a recursive method that would run down the object graph using reflection to persist the object... Putting it back could be much more difficult. Who knows if any of those objects are holding references to unmanaged or system resources. If i was to do anything this nuts, I would go for the .GetFields(...) method on the type.

Another idea...

If you are only doing this to speed up development why not wrap their clases with your own adapter classes. This would allow you to replace the third party libraries with your own simplifed mock classes and allow for better chance for replacement and reuse later.

Sick as it is... This was easier then I thought it would be. (While this works... please consider wrapping the third party classes.)

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 } }
    }
}
Musketry answered 7/4, 2010 at 20:33 Comment(4)
Yeah, I just tried building a quick solution to persist and restore objects based on private values. Persisting is fairly easy; the problem comes with restoring them. If you don't have access to a parametter less constructor it may be impossible.Musketry
I hold no responsibility if the above code causes the world to explode and all life in the universe comes to an abrupt endingMusketry
This only supports string, int, and float right now. you may need to add other native types, or at the very least change how the type selection code works. Enjoy, have fun... and please don't try this at home ;)Musketry
this is vulnerable to stackoverflow due to cyclic object serialization.Bays
S
3

This is one way you could do it:

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

here is the msdn link showing it:

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

Sandrocottus answered 7/4, 2010 at 20:32 Comment(1)
The links don't work anymore. First article: link Second article: linkStutzman
C
2

I don't know if it is overkill for your usage, but I have been playing around with db4o lately. It will persist any object, just call IObjectContainer.Store(object), and it is lightweight and file-based. Does not require any installation.

I haven't had any problems with it yet.

Canvas answered 7/4, 2010 at 20:31 Comment(0)
P
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); 
}
Penury answered 17/11, 2014 at 11:51 Comment(1)
> The object is from a 3rd party library which I cannot change. XmlSerializer could throws InvalidOprationException: [the object] cannot be serialized because it does not have a parameterless constructor.Hagiology

© 2022 - 2024 — McMap. All rights reserved.