简体   繁体   中英

XML serialize base class without knowing derived class

I know that an XmlSerializer will throw an exception if the passed in type does not match the type from when the serializer was created.

My question is: Is it possible to serialize a base type without knowing the derived type? Consider this scenario:

The following classes are in a top-level domain and have few or no dependencies :

public class Serializer
{
    private static string Serialize(Base baseData)
    {
        var serializer = new XmlSerializer(typeof(Base));
        var sb = new StringBuilder();
        using (var writer = XmlWriter.Create(sb))
        {
            serializer.Serialize(writer, baseData);
        }
        return sb.ToString();
    }

    private static string Deserialize(...) { ... }
}

public class Base
{
    public string Name { get; set; }
}

The following derived class has additional dependencies on things like UI components, composition, etc. (more on this later):

[Export(typeof(IMyComponent))]
public class Derived : Base, IMyComponent, INotifyPropertyChanged
{
    public new string Name
    { 
        get { return base.Name; }
        set
        {
            base.Name = value;
            NotifyPropertyChanged("Name");
        }
    }
}

If I invoke the serializer like this:

Base myData = new Derived();
myData.Name = "Foo bar";
Serializer.Serialize(myData);

It throws an exception because I passed in a Derived , even though the parameters and serializer are explicitly set to Base . All I want is to serialize Base , I don't care about whatever is in Derived .

My goal here is to create the derived class with a UI, but only serialize the base class . The reason is that I am deserializing the XML in a separate assembly that must not contain additional dependencies beyond the .NET framework. The serializer and base objects are recreated/copied into this isolated assembly so that the namespaces and types will match, so that when deserialization occurs, the types will be deserialized correctly. But I can't include a copy of Derived because it has dependencies on UI, MEF, other third party libraries, etc.

So, how can I force the serializer to serialize the base class even if I pass in a derived class? Again, I don't care if I lose the information in Derived , all I am concerned about is Base .

One possible solution would be:

public class Serializer
{
    public static string Serialize(Base baseData)
    {
        var baseType = baseData.GetType().BaseType;
        var baseTypeFieldInfo = baseType.GetProperties();
        var newBaseInstance = new Base();
        var newBaseInstanceType = newBaseInstance.GetType();
        //ONLY gets public properties but could be applied to fields
        var newBaseTypeFieldInfo = newBaseInstanceType.GetProperties();
        foreach (var bt in baseTypeFieldInfo)
        {
            foreach (var nbt in newBaseTypeFieldInfo)
            {
                if (nbt.Name == bt.Name)
                {
                    nbt.SetValue(newBaseInstance, bt.GetValue(baseData, null), null);
                }
            }
        }
        var serializer = new XmlSerializer(typeof(Base));
        var sb = new StringBuilder();
        using (var writer = XmlWriter.Create(sb))
        {
            serializer.Serialize(writer, newBaseInstance);
        }
        return sb.ToString();
    }
}

public class Base
{
    public string Name { get; set; }
}


public class Derived : Base
{

    public string AnotherName { get; set; }

    public new string Name
    {
        get { return base.Name; }
        set { base.Name = value; }
    }
}

Since you are looking to only serialize the Base properties, you have to create an instance of Base and map the properties from the derived type. Serializers are programmed to be smart, sometimes too smart in your case.

Look into AutoMapper or just do it manually.

public class Serializer
{
    private static string Serialize(Base baseData)
    {
        var mappedBase = new Base();
        // Do mapping
        mappedBase.Foo = baseData.Foo;

        var serializer = new XmlSerializer(typeof(Base));
        var sb = new StringBuilder();
        using (var writer = XmlWriter.Create(sb))
        {
            serializer.Serialize(writer, mappedBase);
        }
        return sb.ToString();
    }

    private static string Deserialize(...) { ... }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM