C# – Cannot serialize member because it is an interface

asp.netcdeserializationneo4jclientxml-serialization

Follow on question from Error with explicit conversion when using CollectAs<>

Code from WebMethod

return client.Cypher
        .Match("(person:Person)")
        .Where((Person person) => person.Email == username)
        .OptionalMatch("(person)-[:SPEAKS]-(language:Language)")
        .OptionalMatch("(person)-[:CURRENT_LOCATION]-(country:Country)"
        .Return((person, language, country) => new ProfileObject
        {
            Person = person.As<Person>(),
            Language = language.CollectAs<Language>(),
            Country = country.CollectAs<Country>()
        }).Results.ToList();

Code from Country Class:

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

New code from ProfileObject Class:

public class ProfileObject
{
    public Person Person { get; set; }
    public IEnumerable<Node<Language>> Language { get; set; }
    public IEnumerable<Node<Country>> Country { get; set; }
}

This error only happens when I set ProfileObject to return IEnumerable>, if I return it to just

public Country Country {get; set;} 

then it works (but I obviously get duplicated Person entries for each Country object returned.

Anyone able to show me a solution to this problem that doesn't involve me ripping all of the code out and starting again?

Update:

[InvalidOperationException: Neo4jClient.Node`1[Graph.Language] cannot be serialized because it does not have a parameterless constructor.]

[InvalidOperationException: Cannot serialize member 'Graph.ProfileObject.Language' of type 'System.Collections.Generic.List1[[Neo4jClient.Node1[[Graph.Language, Graph, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], Neo4jClient, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]', see inner exception for more details.]
System.Xml.Serialization.StructModel.CheckSupportedMember(TypeDesc typeDesc, MemberInfo member, Type type) +5451673
System.Xml.Serialization.StructModel.CheckSupportedMember(TypeDesc typeDesc, MemberInfo member, Type type) +69
System.Xml.Serialization.StructModel.GetPropertyModel(PropertyInfo propertyInfo) +125
System.Xml.Serialization.StructModel.GetFieldModel(MemberInfo memberInfo) +89
System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter) +618
System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter) +378
System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter) +1799

[InvalidOperationException: There was an error reflecting type 'Graph.ProfileObject'.]
System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter) +1917
System.Xml.Serialization.XmlReflectionImporter.CreateArrayElementsFromAttributes(ArrayMapping arrayMapping, XmlArrayItemAttributes attributes, Type arrayElementType, String arrayElementNs, RecursionLimiter limiter) +263
System.Xml.Serialization.XmlReflectionImporter.ImportArrayLikeMapping(ArrayModel model, String ns, RecursionLimiter limiter) +264
System.Xml.Serialization.XmlReflectionImporter.ImportAccessorMapping(MemberMapping accessor, FieldModel model, XmlAttributes a, String ns, Type choiceIdentifierType, Boolean rpc, Boolean openModel, RecursionLimiter limiter) +5456308
System.Xml.Serialization.XmlReflectionImporter.ImportMemberMapping(XmlReflectionMember xmlReflectionMember, String ns, XmlReflectionMember[] xmlReflectionMembers, Boolean rpc, Boolean openModel, RecursionLimiter limiter) +852
System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, String ns, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, RecursionLimiter limiter) +286

[InvalidOperationException: There was an error reflecting 'MyResult'.]
System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, String ns, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, RecursionLimiter limiter) +979
System.Xml.Serialization.XmlReflectionImporter.ImportMembersMapping(String elementName, String ns, XmlReflectionMember[] members, Boolean hasWrapperElement, Boolean rpc, Boolean openModel, XmlMappingAccess access) +133
System.Web.Services.Protocols.SoapReflector.ImportMembersMapping(XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter, Boolean serviceDefaultIsEncoded, Boolean rpc, SoapBindingUse use, SoapParameterStyle paramStyle, String elementName, String elementNamespace, Boolean nsIsDefault, XmlReflectionMember[] members, Boolean validate, Boolean openModel, String key, Boolean writeAccess) +240
System.Web.Services.Protocols.SoapReflector.ReflectMethod(LogicalMethodInfo methodInfo, Boolean client, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter, String defaultNs) +2893

[InvalidOperationException: Method ProfileServices.My can not be reflected.]
System.Web.Services.Protocols.SoapReflector.ReflectMethod(LogicalMethodInfo methodInfo, Boolean client, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter, String defaultNs) +6173
System.Web.Services.Description.SoapProtocolReflector.ReflectMethod() +137
System.Web.Services.Description.ProtocolReflector.ReflectBinding(ReflectedBinding reflectedBinding) +1776
System.Web.Services.Description.ProtocolReflector.Reflect() +641
System.Web.Services.Description.ServiceDescriptionReflector.ReflectInternal(ProtocolReflector[] reflectors) +685
System.Web.Services.Description.ServiceDescriptionReflector.Reflect(Type type, String url) +118
System.Web.Services.Protocols.DocumentationServerType..ctor(Type type, String uri, Boolean excludeSchemeHostPortFromCachingKey) +230
System.Web.Services.Protocols.DocumentationServerProtocol.Initialize() +434
System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing) +122

[InvalidOperationException: Unable to handle request.]
System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing) +320
System.Web.Services.Protocols.WebServiceHandlerFactory.CoreGetHandler(Type type, HttpContext context, HttpRequest request, HttpResponse response) +171

[InvalidOperationException: Failed to handle request.]
System.Web.Services.Protocols.WebServiceHandlerFactory.CoreGetHandler(Type type, HttpContext context, HttpRequest request, HttpResponse response) +374
System.Web.Services.Protocols.WebServiceHandlerFactory.GetHandler(HttpContext context, String verb, String url, String filePath) +209
System.Web.Script.Services.ScriptHandlerFactory.GetHandler(HttpContext context, String requestType, String url, String pathTranslated) +48
System.Web.HttpApplication.MapHttpHandler(HttpContext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig) +226
System.Web.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +145
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Best Answer

By default, uses DataContractSerializer to serialize to XML, however the error message "Cannot serialize member <x> because it is an interface" is generated by XmlSerializer, so apparently you have switched to that.

You could consider switching back to DataContractSerializer as specified here, which can serialize properties of type IEnumerable<T> as long as the underlying type T can be serialized.

Alternatively, if you don't want to do that, you could modify your ProfileObject class to return proxy arrays for serialization without changing your underlying design:

public class ProfileObject
{
    public Person Person { get; set; }

    [XmlIgnore]
    public IEnumerable<Node<Language>> Language { get; set; }

    [XmlIgnore]
    public IEnumerable<Node<Country>> Country { get; set; }

    [XmlArray("Languages")]
    [XmlArrayItem("Language")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public Node<Language>[] LanguageArray
    {
        get
        {
            if (Language == null)
                return null;
            return Language.ToArray();
        }
        set
        {
            Language = value;
        }
    }

    [XmlArray("Countries")]
    [XmlArrayItem("Country")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public Node<Country>[] CountryArray
    {
        get
        {
            if (Country == null)
                return null;
            return Country.ToArray();
        }
        set
        {
            Country = value;
        }
    }
}

Update

XmlSerializer will only serialize properties with public get and set methods. Since Node.Data is get-only, it can't be serialized by XmlSerializer.

Since you only need to serialize the data and not the Node<TData>, and never need to deserialize, you can use linq to return transform your enumerable of nodes to an array of data for serialization as follows:

public static class NodeExtensions
{
    public static TData [] ToDataArray<TData>(this IEnumerable<Node<TData>> nodes)
    {
        if (nodes == null)
            return null;
        return nodes.Select(n => n.Data).ToArray();
    }
}

public class ProfileObject
{
    public Person Person { get; set; }

    [XmlIgnore]
    public IEnumerable<Node<Language>> Language { get; set; }

    [XmlIgnore]
    public IEnumerable<Node<Country>> Country { get; set; }

    [XmlArray("ArrayOfLanguage")]
    [XmlArrayItem("Language")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public Language [] LanguageArray
    {
        get
        {
            return Language.ToDataArray();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    [XmlArray("ArrayOfCountry")]
    [XmlArrayItem("Country")]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public Country [] CountryArray
    {
        get
        {
            return Country.ToDataArray();
        }
        set
        {
            throw new NotImplementedException();
        }
    }
}