2

We're working on a project with a modestly-complex model, the core class of which has a number of properties that are lists of other associated classes. We're using EF 6 (code first) on the back end, and, following guidance on how to construct classes for that environment, we heavily leveraged ICollection<T>:

public class Product
{
    // a few normal properties
    public string Name { get; set; }
    public string Description { get; set; }
    public string Code { get; set; }
    // a large list of ICollection<T> properties
    public virtual ICollection<Rule> Rules { get; set; }
    public virtual ICollection<Update> Updates { get; set; }
    public virtual ICollection<Reference> References { get; set; }
    // more ICollections from here...
}

Several of the classes referenced by ICollection<T> have collections within themselves (e.g. Rule has ICollection<Condition> Conditions), creating a fairly deep and complex tree.

Now that we have our model nearly finalized, we've moved on to developing business logic and a UI (ASP.NET MVC). One of the required capabilities of the system is to serialize/deserialize XML to interface with another system. But we've discovered that XmlSerialization.Serialize() doesn't work, complaining that it can't serialize something that uses an interface.

When we started this whole project, knowing that serialization was going to be a factor, we constructed a set of XSDs and used xsd.exe to generate classes for us, which we've since modified heavily. However, the utility put in a bunch of tagging that was supposed to aid in serialization, and we kept it all:

[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute("product", Namespace = "", IsNullable = false)]
public class Product
{
    ....
}

Unfortunately, we're encountering the problem of not being able to serialize, even though we're (theoretically) telling the compiler that, yes indeed, this class is serializable. And each of the sub-classes has a similar set of tags. So, more research was undertaken...

The solutions offered here seem like they come with a lot of baggage. Other SO answers on the subject just seem to describe the difference between ICollection<T> and Collection<T>, without providing much guidance on why you'd want to avoid the concrete class. I'm considering just migrating everything to using Collection<T> instead of ICollection<T>. Am I going to cause myself problems for the future if I do this?

Community
  • 1
  • 1
J.D. Ray
  • 697
  • 1
  • 8
  • 22
  • 1
    Perhaps look into using the `DataContractSerializer` instead. See http://stackoverflow.com/a/9306746/468973 – Magnus Mar 12 '15 at 17:15

1 Answers1

0

Have you tried IXmlSerializable? From this interface you can control what you write and read. I am not sure if this can help your problem.

    public CSortedList<string, CBasicStockData> Stocks { get; set; }
    public CSortedList<string, CIndustrySectorExchangeInfo> Exchanges { get; set; }
    public CSortedList<string, CIndustrySectorExchangeInfo> Industries { get; set; }
    public CSortedList<string, CIndustrySectorExchangeInfo> Sectors { get; set; }

    public void WriteXml(XmlWriter writer)
    {
        try
        {
            ///////////////////////////////////////////////////////////
            writer.WriteStartElement("Stocks");
            writer.WriteAttributeString("num", Stocks.Count.ToString());
            foreach (var kv in Stocks)
            {
                writer.WriteStartElement("item");
                foreach (var p in kv.Value.WritableProperties)
                {
                    var value = p.GetValue(kv.Value);
                    var str = (value == null ? string.Empty : value.ToString());
                    writer.WriteAttributeString(p.Name, str);
                }
                writer.WriteEndElement();
            }
            writer.WriteEndElement();

            ///////////////////////////////////////////////////////////
            foreach (var propInfo in this.WritableProperties)
            {
                if (propInfo.Name == "Stocks") continue;
                dynamic prop = propInfo.GetValue(this);

                writer.WriteStartElement(propInfo.Name);
                writer.WriteAttributeString("num", prop.Count.ToString());
                foreach (var kv in prop)
                {
                    writer.WriteStartElement("item");
                    foreach (var p in kv.Value.WritableProperties)
                    {
                        var value = p.GetValue(kv.Value);
                        var str = (value == null ? string.Empty : value.ToString());
                        writer.WriteAttributeString(p.Name, str);
                    }
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw ex;
        }
    }

    public void ReadXml(XmlReader reader)
    {
        var propName = string.Empty;
        while (reader.Read() &&
            !(reader.NodeType == XmlNodeType.EndElement && reader.LocalName == this.GetType().Name))
        {
            if (reader.Name != "item")
            {
                propName = reader.Name;
                continue;
            }

            switch (propName)
            {
                case "Stocks":
                    {
                        var obj = new CBasicStockData();
                        foreach (var propInfo in obj.WritableProperties)
                        {
                            var value = reader.GetAttribute(propInfo.Name);
                            if (value == null)  //we may add new property to class after the file is created
                                continue;
                            propInfo.SetValue(obj, Convert.ChangeType(value, propInfo.PropertyType));
                        }
                        this.Stocks.Add(obj.Symbol, obj);
                        break;
                    }
                case "Exchanges":
                case "Industries":
                case "Sectors":
                    {
                        var obj = new CIndustrySectorExchangeInfo();
                        foreach (var p in obj.WritableProperties)
                        {
                            var value = reader.GetAttribute(p.Name);
                            if (value == null)
                                continue;
                            p.SetValue(obj, Convert.ChangeType(value, p.PropertyType));
                        }
                        var propInfo = this.WritableProperties.Find(x => x.Name == propName);
                        dynamic prop = propInfo.GetValue(this);
                        prop.Add(obj.Name, obj);
                        break;
                    }
                default:
                    break;
            }
        }
    }

    public static string XML_Serialize<T>(string filename, T myObject) where T : IXmlSerializable
    {
        XmlSerializer xmlSerializer = new XmlSerializer(myObject.GetType());
        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;

        using (StringWriter stringWriter = new StringWriter())
        using (XmlWriter writer = XmlWriter.Create(stringWriter, settings)) {
            xmlSerializer.Serialize(writer, myObject);
            var xml = stringWriter.ToString(); // Your xml
            File.WriteAllText(filename, xml);
            return xml;
        }
    }
    public static void XML_DeSerialize<T>(string filename, out T myObject) where T : IXmlSerializable
    {
        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
        using (StreamReader reader = new StreamReader(filename)) {
            myObject = (T)xmlSerializer.Deserialize(reader);
        }
    }
Ping
  • 46
  • 4