Cannot serialize member.... because it is an interface
I have been having this problem and been pulling my hair out over it. I have the followin error:
Exception Details: System.NotSupportedException: Cannot serialize member HannaPrintsDataAccess.Customer.CustomerAddresses of type System.Collections.Generic.IList`1[[HannaPrintsDataAccess.CustomerAddress, HannaPrintsDataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.
Source Error:
Line 196: Customer customer = OperationsManager.Instance.CustomerService.GetCustomer(7)开发者_运维问答; Line 197: Line 198: string xml = OperationsManager.Instance.CustomerService.GetCustomerAddressesXml(CustomerAddress.FindAll()); Line 199: Line 200: Order order = OperationsManager.Instance.OrderService.CreateOrderFromCart(xml);
Source File: c:\HostingSpaces\greetwus\galadavetiye.com\wwwroot\HannaPrints\HannaPrints\WebUI\CreateGreetingCard.aspx.cs Line: 198
Stack Trace:
[NotSupportedException: Cannot serialize member HannaPrintsDataAccess.Customer.CustomerAddresses of type System.Collections.Generic.IList`1[[HannaPrintsDataAccess.CustomerAddress, HannaPrintsDataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.]
[InvalidOperationException: Cannot serialize member 'HannaPrintsDataAccess.Customer.CustomerAddresses' of type 'System.Collections.Generic.IList`1[[HannaPrintsDataAccess.CustomerAddress, HannaPrintsDataAccess, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details.] System.Xml.Serialization.StructModel.CheckSupportedMember(TypeDesc typeDesc, MemberInfo member, Type type) +889917 System.Xml.Serialization.StructModel.GetPropertyModel(PropertyInfo propertyInfo) +132........
I have changed all my IList's to List's to see if that would do anything, but it didnt, infact, it didnt even take a second to load after making those changes, im guessing because the error happens even before it gets to that part. I checked my remote files to see if it was uploading correctly and it was.
Here is the code:
using System;
using System.Collections.Generic;
using Castle.ActiveRecord;
namespace HannaPrintsDataAccess {
public partial class Customer {
private IList _customerAddresses;
public CustomerAddress GetPrimaryCustomerAddress()
{
foreach (CustomerAddress address in _customerAddresses)
{
if (address.IsPrimary)
return address;
}
return null;
}
[HasMany(typeof(CustomerAddress), ColumnKey = "CustomerId", Table = "Customer")]
public virtual IList<CustomerAddress> CustomerAddresses
{
get
{
return this._customerAddresses;
}
set
{
this._customerAddresses = value;
}
}
}
}
The error happens when this code is activated:
protected void orderButton_Click(object sender, EventArgs e)
{
Customer customer = OperationsManager.Instance.CustomerService.GetCustomer(7);
string xml = OperationsManager.Instance.CustomerService.GetCustomerAddressesXml(CustomerAddress.FindAll());
Order order = OperationsManager.Instance.OrderService.CreateOrderFromCart(xml);
OperationsManager.Instance.CartService.MoveCart("MyDesigns");
Response.Redirect("~/Customer/PayByCreditCard.aspx?orderGuid=" + order.OrderGuid);
}
The CustomerAddress class:
using System.IO;
using System.Xml.Serialization;
using Castle.ActiveRecord;
namespace HannaPrintsDataAccess
{
public partial class CustomerAddress
{
public string ToXml()
{
XmlSerializer serializer = new XmlSerializer(GetType());
MemoryStream memoryStream = new MemoryStream();
serializer.Serialize(memoryStream, this);
memoryStream.Seek(0, SeekOrigin.Begin);
return new StreamReader(memoryStream).ReadToEnd();
}
[BelongsTo("CustomerId")]
public virtual Customer Customer { get; set; }
}
}
In the code you posted, the type of CustomerAddresses
is IList<CustomerAdress>
. That's an interface. Like the error message says, you can't serialize an interface.
In some circumstances, one might not be interested in serializing ALL fields of an object. In that case, the C# syntax to explicitly exclude a field from the serialization of an object is the following:
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes{ XmlIgnore = true };
attrOverrides.Add(type, member, attrs); // "type" = type of the class that contains the member
XmlSerializer serializer = new XmlSerializer(obj.GetType(), attrOverrides);
[...]
For example, to exclude, e.g. for privacy reasons, the "Password" field from the serialization of an "obj" object we should write:
attrOverrides.Add(obj.GetType(), "Password", attrs);
For fields not declared directly in the object but inherited, the "type" parameter refers to the ancestor in which the field is declared. For example, to avoid, in an object that is a descendant of System.ComponentModel.Component, the error "Cannot serialize member System.ComponentModel.Component.Site of type System.ComponentModel.ISite because it is an interface", the syntax to use is:
attrOverrides.Add(typeof(System.ComponentModel.Component), "Site", attrs);
In general, the XMLSerializer cannot serialize an object of a class that exposes interface-type fields. Nevertheless, if someone still wants to serialize (with XMLSerializer) an object of this type (e.g. for log reasons) and has no problem ignoring fields of the interface type in serialization, the following function automatically ignores fields of this type (NB: of course the final result will NOT be the serialization of the initial object, but the serialization EXCLUDING the ignored parts, which is not the same thing):
public static string Serialize<T>(T obj, bool ignoreInterfaceTypeFields = false, List<Tuple<Type, string>> ignoreTypeList = null)
{
string retValue = string.Empty;
try
{
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes{ XmlIgnore = true };
ignoreTypeList?.ForEach(t => attrOverrides.Add(t.Item1, t.Item2, attrs)); // ignore fields in ignoreTypeList
if (ignoreInterfaceTypeFields)
{
foreach (var t in obj.GetType().GetProperties())
{
if (t.PropertyType.IsInterface)
if (attrOverrides[t.DeclaringType, t.Name] is null)
attrOverrides.Add(t.DeclaringType, t.Name, attrs); // ignore interface type fields
}
}
XmlSerializer serializer = new XmlSerializer(obj.GetType(), attrOverrides);
using (var sw = new StringWriter())
{
using (XmlTextWriter writer = new XmlTextWriter(sw) { Formatting = Formatting.Indented })
{
serializer.Serialize(writer, obj);
retValue = sw.ToString();
}
}
}
catch (Exception ex) { throw ex; }
return retValue;
}
// declared in non-generic, non-nested static classes
public static string Serialize<T>(this T obj, bool ignoreInterfaceTypeFields = false, List<Tuple<Type, string>> ignoreTypeList = null)
{
return Serialize<T>(obj, ignoreInterfaceTypeFields, ignoreTypeList);
}
This Serialize method can be called without params:
sClient = client.Serialize();
or manually excluding unwanted fields:
var ignoreTypeList = new List<Tuple<Type, string>>
{
new Tuple<Type, string>(client.GetType(), "Password"), // unwanted field (directly declared in "Client" class)
new Tuple<Type, string>(typeof(System.ComponentModel.Component), "Site") // unwanted field (declared in "System.ComponentModel.Component" ancestor class of "Client" class)
};
sClient = client.Serialize(false, ignoreTypeList);
or excluding unwanted fields AND interface type fields:
var ignoreTypeList = new List<Tuple<Type, string>>
{
new Tuple<Type, string>(client.GetType(), "Password") // unwanted field (directly declared in "Client" class)
};
sClient = client.Serialize(true, ignoreTypeList);
NOTE: with framework 4.7.2 or later (or using System.ValueTuple nuget) the syntax of Tuples can be made more readable:
public static string Serialize<T>(T obj, bool ignoreInterfaceTypeFields = false, List<(Type type, string member)> ignoreTypeList = null)
and:
var ignoreTypeList = new List<(Type, string)>
{
new (client.GetType(), "Password"),
new (typeof(System.ComponentModel.Component), "Site")
};
Following options fail with XML serializer:
public IList<CustomerAddresses> CustomerAddresses { get; set; }
public IEnumerable<CustomerAddresses> CustomerAddresses { get; set; }
Use array like below:
public CustomerAddresses[] CustomerAddresses { get; set; }
Following works but not SONAR/Quality complaint:
public List<CustomerAddresses> CustomerAddresses { get; set; }
Use [XmlIgnore] Annotation if you want to use your property without serializing it.
Not the source of your problem, but you need
using (MemoryStream memoryStream = new MemoryStream())
{
serializer.Serialize(memoryStream, this);
memoryStream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(memoryStream))
{
return reader.ReadToEnd();
}
}
精彩评论