0

I have an object like this:

public partial class CableApplication : StateObject
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public CableApplication()
    {
        this.CableProperties = new HashSet<CableProperty>();
    }

    public int Id { get; set; }
    public int ProjectId { get; set; }
    public string Application { get; set; }
    public byte[] ts { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<CableProperty> CableProperties { get; set; }
    public virtual Project Project { get; set; }
}

which is created in .edmx file automatically in database-first Visual Studio C# project. I want to export all the data of CableApplication into an XML file.

I wrote this code in the service:

public string ExportToXml<T>(T obj)
{
    using (var stringwriter = new System.IO.StringWriter())
    {
        TextWriter writer = new StreamWriter(@"d:\\temp\\check.xml");
        var serializer = new XmlSerializer(typeof(T));
        serializer.Serialize(stringwriter, obj);
        writer.Close();

        return stringwriter.ToString();
    }           
}

And this code in the frontend project:

private void exporToXMLToolStripMenuItem_Click(object sender, EventArgs e)
{
    using (ICableService client = new CableService())
    {                
        var applications = client.GetCableApplications(searchList.ToArray(), null, "").ToList();    // I get the list of cable Applications . works fine

        var str = client.ExportToXml(applications);              
    }
}

But I get the following error:

Cannot serialize member 'Cable1Layer.Domain.CableApplication.CableProperties' of type 'System.Collections.Generic.ICollection`1[[Cable1Layer.Domain.CableProperty, Cable1Layer.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details

There was an error reflecting type 'Cable1Layer.Domain.CableApplication'

How should I serialiaze this object?

6
  • Ca you include the specificatin of the class CableProperty as well, please? Commented Feb 25, 2024 at 11:27
  • @JayV: it is an object with its own Properties and naviagtion properties Commented Feb 25, 2024 at 11:34
  • You defined a CableApplication, and the function client.GetCableApplications(...).ToList() does return a list of those things? Commented Feb 25, 2024 at 13:41
  • @Luuk: yes, var applications has the the list of applications fetched from db successfull without error Commented Feb 25, 2024 at 14:31
  • You cannot serialize a List. A well formed xml has only one root tag. You list has multiple tags at root. What may work is : XmlWriterSettings settings = new XmlWriterSettings(); settings.ConformanceLevel = ConformanceLevel.Fragment; XmlWriter writer = XmlWriter.Create("filename", settings); Commented Feb 25, 2024 at 14:35

1 Answer 1

0

The innermost error message (not shown in your question) is self explanatory:

System.NotSupportedException: Cannot serialize member CableApplication.CableProperties of type ICollection<CableProperty> because it is an interface.

XmlSerializer cannot serialize interfaces because, not being concrete types, they cannot be constructed during deserialization. For more see

As a workaround you could introduce a serializable surrogate property and mark the original property with [XmlIgnore], however since your class was auto-generated from an .edmx file you probably don't want to modify any of the auto-generated code. Thus what you can do is:

  • Take advantage of the fact that CableApplication was generated as a partial class to add a serializable surrogate property in a manually created partial file.
  • Use XmlAttributeOverrides to suppress serialization of the non-serializable interface property.

To do this, first extend CableProperties as follows:

public partial class CableApplication
{
    public CableProperty [] CablePropertiesArray
    {
        get { return CableProperties.ToArray(); }
        set
        {
            CableProperties.Clear();
            if (value != null)
            {
                foreach (var item in value)
                    CableProperties.Add(item);
            }
        }
    }
}

Then add the following XmlSerializer factory:

public static class XmlSerializerFactory
{
    readonly static object lockObject = new object();
    readonly static Dictionary<Type, XmlSerializer> SerializerDictionary = new Dictionary<Type, XmlSerializer>();
    
    public static XmlSerializer CreateSerializer(Type type)
    {
        lock (lockObject)
        {
            XmlSerializer serializer;
            if (SerializerDictionary.TryGetValue(type, out serializer))
                return serializer;
            var overrides = new XmlAttributeOverrides()
                .AddCableApplicationOverrides()
                // Add customizations for other types as needed here.
                ;
            SerializerDictionary.Add(type, serializer = new XmlSerializer(type, overrides));
            return serializer;
        }
    }
    
    static XmlAttributeOverrides AddCableApplicationOverrides(this XmlAttributeOverrides overrides)
    {
        overrides.Add(typeof(CableApplication), 
                      "CableProperties", // Use nameof(CableApplication.CableProperties) if available in your C# version
                      new XmlAttributes { XmlIgnore = true });
        overrides.Add(typeof(CableApplication), 
                      "CablePropertiesArray", // Use nameof(CableApplication.CablePropertiesArray) if available in your C# version
                      new XmlAttributes { XmlArray = new XmlArrayAttribute { ElementName = "CableProperties" } }); // Use nameof(CableApplication.CableProperties) if available in your C# version
        return overrides;
    }
}

And finally modify ExportToXml<T>(T obj) to use the serializer factory:

public string ExportToXml<T>(T obj)
{
    using (var stringwriter = new System.IO.StringWriter())
    {
        // You never actually write anything to the StreamWriter so I'm not sure what is going in here.  
        // I commented this code out since I did not have a d:\temp\check.xml file
        //TextWriter writer = new StreamWriter( @"d:\\temp\\check.xml");
        var serializer = XmlSerializerFactory.CreateSerializer(typeof(T));
        serializer.Serialize(stringwriter, obj);
        //writer.Close();

        return stringwriter.ToString();
    }           
}

Note that, when constructing serializer using XmlAttributeOverrides, it is necessary to statically cache and reuse the serializer for reasons explained in Memory Leak using StreamReader and XmlSerializer.

Demo fiddle here.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.