无法序列化成员 <x>,因为它是一个接口

Cannot serialize member <x> because it is an interface

关注

的问题

来自 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();

国家代码Class:

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

来自 ProfileObject 的新代码 Class:

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

只有当我将 ProfileObject 设置为 return IEnumerable> 时才会发生此错误,如果我 return 它只是

public Country Country {get; set;} 

然后它起作用了(但我显然为每个国家/地区对象得到了重复的 Person 条目 returned。

任何人都可以向我展示解决此问题的方法,而无需我撕掉所有代码并重新开始?

更新:

[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

默认情况下, uses DataContractSerializer to serialize to XML, however the error message "Cannot serialize member <x> because it is an interface" is generated by XmlSerializer,显然您已经切换到那个。

您可以考虑切换回 here 指定的 DataContractSerializer,只要基础类型 T 可以序列化,它就可以序列化类型 IEnumerable<T> 的属性。

或者,如果您不想这样做,您可以将 ProfileObject class 修改为 return 代理数组以进行序列化,而无需更改底层设计:

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;
        }
    }
}

更新

XmlSerializer 将仅使用 public getset 方法序列化属性。由于 Node.Data 是 get-only,它不能被 XmlSerializer.

序列化

因为你只需要序列化数据而不是 Node<TData>,并且永远不需要反序列化,你可以使用 linq 来 return 将你的可枚举节点转换为数据数组以进行序列化如下:

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();
        }
    }
}

因此,您不需要对 Node<T> class 添加任何更改,它应该可以很好地反序列化所有内容。

所以,这是我当前的设置:

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

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

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

public class Person
{
    public int Id { get; set; }
    public string Email { get; set; }
}

您没有定义 CountryPerson,所以我编造了它们,但本质上我认为它们非常接近。现在我将我的数据库初始化为:

(PERSON)-[:SPEAKS]->(ENGLISH)
(PERSON)-[:SPEAKS]->(GERMAN)
(PERSON)-[:CURRENT_LOCATION]->(GERMANY)

首先,运行宁你的查询'as is'(即直接从这个页面复制)对我来说很好,在序列化方面 - 你能确认你的LanguagePersonCountryProfileObject 是您使用默认构造函数还是显式无参数构造函数定义的?

至于你期待的结果 - 我猜你在 ProfileObject 之后有 2 个 Language 个对象,但只有 1 个 Country。现在,您编写的查询将像这样转换为 Cypher:

MATCH (person:Person)
WHERE (person.Email = "THE EMAIL ADDRESS HERE")
OPTIONAL MATCH (person)-[:SPEAKS]-(language:Language)
OPTIONAL MATCH (person)-[:CURRENT_LOCATION]-(country:Country)
RETURN person AS Person, collect(language) AS Language, collect(country) AS Country

如果你在 Neo4j 管理器 (localhost:7474) 中 运行 并切换到 'rows' 视图(从图一)你会看到你实际上得到了返回的数据形式你进入客户端。

我会在 neo4j 管理视图中调整查询,直到你得到你想要的。不返回 CollectAs 并在之后执行 LINQ .Group 可能是值得的。