0
votes

For example

[DataContract]
public abstract class BaseClass
{
    public abstract string id { get; set; }
}

[DataContract(Name = "class1")]
public class concreteClass1 : BaseClass
{
    public concreteClass1() { }

    [DataMember]
    public override string id { get; set; }

    [DataMember]
    public string prop1 { get; set; }

    [DataMember]
    public string prop2 { get; set; }

}

[DataContract(Name = "class2")]
public class concreteClass2 : BaseClass
{
    public concreteClass2() { }

    [DataMember]
    public override string id { get; set; }

    [DataMember]
    public string prop1 { get; set; }

    [DataMember]
    public string prop2 { get; set; }

}

When I try to serialize a dictionary containing one of the concrete classes like this

static public void Main(string[] args){

    Dictionary<string, BaseClass> items = new Dictionary<string, BaseClass>();
    items.Add("1", new concreteClass1() { id = "1", prop1 = "blah1" });
    items.Add("11", new concreteClass1() { id = "11", prop1 = "blah11" });

    var serializer = new DataContractSerializer(items.GetType());
    string xmlString = string.Empty;
    using (var sw = new StringWriter())
    {
        using (var writer = new XmlTextWriter(sw))
        {
            writer.Formatting = System.Xml.Formatting.Indented;
            serializer.WriteObject(writer, items );
            writer.Flush();
            xmlString = sw.ToString();
        }
    }

}

I get this error when trying to WriteObject

Type 'ConsoleTest.Program+Base' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.

Is there a way to solve this?

EDIT: I also tried using KnownType on the base class but it didn't work

    [DataContract]
    [KnownType(typeof(concreteClass1))]
    [KnownType(typeof(concreteClass2))]
    public abstract class BaseClass
    {
        public abstract string id { get; set; }
    }
1
what is TL in the following line of code?: serializer.WriteObject(writer, TL);iCode
I figured it was but needed to clarify. I copied your code in a test project...I added a line of code to write the xmlString to the Console and it worked for me. I didn't want to copy paste the XML here, but it worked. Try wrapping your using (var sw = new StringWriter()) code block in a try catch and view exception details.iCode
Ah, I see what @Monty is talking about. id is serialized (I removed the xmlns declarations). Your id property is in the Key node: <ArrayOfKeyValueOfstringBaseClassDuhsZsT3> <KeyValueOfstringBaseClassDuhsZsT3> <Key>1</Key> <Value xmlns:d3p1="schemas.datacontract.org/2004/07/WindowsFormsApplication2" i:type="d3p1:class1"> <d3p1:prop1>blah1</d3p1:prop1> <d3p1:prop2 i:nil="true" /> </Value> </KeyValueOfstringBaseClassDuhsZsT3>iCode
@erotavlas OK... well i don't know what i changed (except to replace 'TL' for 'items') but my code is working....Monty
@Monty damn, its possible it was a typo all alongerotavlas

1 Answers

1
votes

try this...

using ....

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

classes...

[DataContract]
[KnownType(typeof(concreteClass1))]
[KnownType(typeof(concreteClass2))]
public abstract class BaseClass
{
    [DataMember]
    public abstract string id { get; set; }
}

[DataContract(Name = "class1")]
public class concreteClass1 : BaseClass
{
    public concreteClass1() { }

    [DataMember]
    public override string id { get; set; }

    [DataMember]
    public string prop1 { get; set; }

    [DataMember]
    public string prop2 { get; set; }

}

[DataContract(Name = "class2")]
public class concreteClass2 : BaseClass
{
    public concreteClass2() { }

    [DataMember]
    public override string id { get; set; }

    [DataMember]
    public string prop1 { get; set; }

    [DataMember]
    public string prop2 { get; set; }

}

code....

    static void Main(string[] args)
    {
        Dictionary<string, BaseClass> items = new Dictionary<string, BaseClass>();
        items.Add("1", new concreteClass1() { id = "1", prop1 = "blah1" });
        items.Add("11", new concreteClass1() { id = "11", prop1 = "blah11" });
        // this should work too....
        items.Add("999", new concreteClass2() { id = "999", prop1 = "blah999" });
        items.Add("888", new concreteClass2() { id = "888", prop1 = "blah888" });

        //Serialize(items);

        var serializer = new DataContractSerializer(items.GetType());
        string xmlString = string.Empty;
        try
        {
            using (var sw = new StringWriter())
            {
                using (var writer = new XmlTextWriter(sw))
                {
                    writer.Formatting = System.Xml.Formatting.Indented;
                    serializer.WriteObject(writer, items);
                    writer.Flush();
                    xmlString = sw.ToString();
                }
            }
        }
        catch (Exception)
        {

            throw;
        }
    }

///////////////////// UPDATE /////////////////////

As a bit of a bonus (don't feel I eared my 25 points yet) here are two functions that will serialise and deserialise a generic object.....

        public static void Serialize<T>(T data)
        {
            try // try to serialize the collection to a file
            {
                using (Stream  stream = File.Open("data.xml", FileMode.Create))
                {
                    // create DataContractSerializer
                    DataContractSerializer serializer = new DataContractSerializer(typeof (T));
                    // serialize the collection (EmployeeList1) to file (stream)
                    serializer.WriteObject(stream, data);
                }
            }
            catch (IOException)
            {
            }
        }

        public static T Deserialize<T>() where T : new()
        {
            // Create an instance of T
            T ReturnListOfT = CreateInstance<T>();

            // Try to Deserialize from file stream
            try
            {
                using (Stream stream = File.Open("data.xml", FileMode.Open))
                {
                    // create DataContractSerializer
                    DataContractSerializer serializer = new DataContractSerializer(typeof (T));
                    // deserialize the collection (Employee) from file (stream)
                    ReturnListOfT = (T)serializer.ReadObject(stream);
                }
            }
            catch (IOException)
            {
            }

            return (T)ReturnListOfT;
        }

        // function to create instance of T
        public static T CreateInstance<T>() where T : new()
        {
            return (T)Activator.CreateInstance(typeof(T));
        }

instead of having to modify the XML manually, you could deserialise the object (from a file, in the example 'data.xml') using your existing classes and create a User Interface to allow the user to modify the properties of the object\classes, then re-save\ serialise the modified object back to a file.....