views:

242

answers:

2

How do I cast an instance of an object and actually make it that type of object?

I have a class myClass1 that is the base class for myClass2 and myClass3. I want to use myClass1 for auditing, for auditing all I want is the data from myClass1. Because myClass2 and myClass3 inherit from myClass1 you can set an instance of myClass1 to an instance of myClass2 example:

myClass2 foo = new myClass2();
foo.prop1 = "some data";
foo.prop2 = "some More Data";

myClass1 bar = foo;

the problems come because I'm using a generic

 public static IXPathNavigable SerializeGeneric<T>(T serializableObject)
    {
        String XmlizedString = "Error processing request";
        XmlDocument XMLObject = new XmlDocument();
        try
        {
            MemoryStream memoryStream = new MemoryStream();
            XmlSerializer xs = new XmlSerializer(serializableObject.GetType());

to pass the class when I Serialize it and XmlSerializer throws an error because even though I have cast it as a myClass1 the underlying object is still a myClass2 you can see this by casting it an object and then checking the type and XmlSerializer get's confused because I'm telling it to make it a class1 be though it's own reflection it sees it as a myClass2

myClass2 foo = new myClass2();
foo.prop1 = "some data";
foo.prop2 = "some More Data";

myClass1 bar = foo;
object obj = bar;
string name = obj.GetType().Name;

the value of name is "myClass2" which makes sense seeing that the data in the memory is really a myClass2, underneath bar is just a pointer to a myClass2 object. Without creating a new instance and setting the values of that new instance to that object like

myClass1 bar = new myClass1(){prop1=foo.prop1, prop2=foo.prop2};

I really don't want to do it that way.

+1  A: 

No idea if this will work but try changing it to:

XmlSerializer xs = new XmlSerializer(typeof(T));

this will tell the serializer create a serializer instance of whatever type you specify. Though i'm not sure if the serializer will even do this.

Edit: Provided you call

SerializeGeneric<MyClass1>(foo);

Edit Again:

Just tried it with this:

    public void Test()
    {
        var obj = new Foo2() { Prop1 = "Test", Prop2 = "Test2" };

        SerializeGeneric((Foo1)obj);
    }

    private void SerializeGeneric<T>(T obj)
    {
        StringWriter writer = new StringWriter();    
        XmlSerializer xs = new XmlSerializer(typeof(T));
        xs.Serialize(writer, obj);

        Console.WriteLine(writer.ToString());
    }


    public class Foo1
    {
        public string Prop1 { get; set; }
    }

    public class Foo2 : Foo1
    {
        public string Prop2 { get; set; }
    }

It throws an exception of "Unexpected Type". It turns out the serializer won't serialize an object as a different type. Not sure of any way to make it do it.

I suppose you could write a custom serializer, or write a simple reflection method that does a memberwiseclone-ish operation that only copies the properties from foo1 that you want.

Interestingly it doesn't error if you add [XmlInclude(typeof(Foo2))] to the Foo1 declaration, though it outputs this gibberish:

<Foo1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="Foo2">
  <Prop1>Test</Prop1>
  <Prop2>Test2</Prop2>
</Foo1>

Which is a Foo1 declaration, with Foo1 & Foo2 properties, with the type declared as Foo2... interesting.

Last one:

This works, though im not sure i'd recommend it.

    public void Test ()
    {
        var obj = new Foo2() { Prop1 = "Test", Prop2 = "Test2" };

        SerializeGeneric(ShallowCopy<Foo1>(obj));
    }

    private T ShallowCopy<T>(object input)
        where T : class, new()
    {
        T newObj = Activator.CreateInstance<T>();
        Type oldType = input.GetType();
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetField | BindingFlags.SetField;
        var oldProperties = oldType.GetProperties(flags);

        foreach (var pd in typeof(T).GetProperties(flags))
        {
            var oldPd = oldProperties.FirstOrDefault(x=>x.Name == pd.Name && x.PropertyType == pd.PropertyType);

            if(oldPd != null)
               pd.SetValue(newObj, oldPd.GetValue(input, null), null);
        }

        return newObj;
    }

This gives you:

<Foo1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  <Prop1>Test</Prop1>
</Foo1>

which looks perfect.

Brian Rudolph
I already tried that and yes you can do that, and it errors for the same reason, by using a generic like this it sets the type of serializableObject to T so gettig the type of T is the same as getting the Type of serializableObject,
Bob The Janitor
Not if you explicitly declare the type.
Brian Rudolph
at least I don't feel as bad now that I'm not the only one who can't figure this out
Bob The Janitor
SWEET!!! it worked, I don't like having to couple my base classes to the inherited classes, but in this case it's not that big of a deal
Bob The Janitor
The shallow copy might be prettier.
Brian Rudolph
A: 

To do xml serialization properly when you have inheritance, there are a couple of guidelines:

  1. Always create XmlSerializer with typeof(Base) (when serializing or deserializing)
  2. Set the XmlInclude(typeof(Derived)) attribute on the Base class (for every derived class)

This allows you to call Deserialize any xml that represents any object in the inheritance hierarchy. There are alternatives (such as passing in all the possible derived types to the constructor of XmlSerializer, but its debatable if that's an improvement over the attribute)

Nader Shirazie