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.List
1[[Neo4jClient.Node
1[[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
By default, asp.net 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();
}
}
}