具有对象类型成员的 DataContractJsonSerializer

DataContractJsonSerializer with object type member

我有以下序列化方法:

private string Serialize(Message message)
{
    byte[] json;

    using (var ms = new MemoryStream())
    {
        var ser = new DataContractJsonSerializer(typeof(Message));
        ser.WriteObject(ms, message);
        json = ms.ToArray();
    }

    return Encoding.UTF8.GetString(json, 0, json.Length);
}

我正在尝试序列化以下对象:

[DataContract]
public class Message
{
    [DataMember(Name = "technical", Order = 1)]
    public Technical Technical;

    [DataMember(Name = "payload", Order = 2)]
    public object Payload;
}

[DataContract]
public class Technical
{
    [DataMember(Name = "topic", Order = 1)]
    public string Topic { get; set; }

    [DataMember(Name = "nature", Order = 2)]
    public string Nature { get; set; }

    [DataMember(Name = "event_id", Order = 3)]
    public string EventId { get; set; }
}

我正面临与 object 参数相关的序列化异常,该参数包含一个 AuthorizationRequest 对象,该对象也具有 DataContractDataMember。这是完整的异常堆栈跟踪:

L'exception System.Runtime.Serialization.SerializationException n'a pas été gérée par le code utilisateur HResult=-2146233076
Message=Le type 'AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities.AuthorizationRequest' avec le nom de contrat de données 'AuthorizationRequest:http://schemas.datacontract.org/2004/07/AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities' n'est pas attendu. Utilisez un DataContractResolver si vous utilisez DataContractSerializer ou ajoutez tous les types non connus statiquement à la liste des types connus, par exemple en utilisant l'attribut KnownTypeAttribute ou en les ajoutant à la liste des types connus qui est transmise au sérialiseur.
Source=System.Runtime.Serialization StackTrace: à System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) à System.Runtime.Serialization.Json.XmlObjectSerializerWriteContextComplexJson.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) à System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) à System.Runtime.Serialization.XmlObjectSerializerWriteContextComplex.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) à WriteMessageToJson(XmlWriterDelegator , Object , XmlObjectSerializerWriteContextComplexJson , ClassDataContract , XmlDictionaryString[] ) à System.Runtime.Serialization.Json.JsonClassDataContract.WriteJsonValueCore(XmlWriterDelegator jsonWriter, Object obj, XmlObjectSerializerWriteContextComplexJson context, RuntimeTypeHandle declaredTypeHandle) à System.Runtime.Serialization.Json.XmlObjectSerializerWriteContextComplexJson.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) à System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) à System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph) à System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph) à System.Runtime.Serialization.XmlObjectSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) à System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) à System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject(XmlDictionaryWriter writer, Object graph) à System.Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject(Stream stream, Object graph) à AF.WS.ProjetEtDevis.DAP.MNG.BusinessLogic.AuthorizationRequestMngService.Serialize(Message message) dans C:\src\Git\AF.WS.ProjetEtDevis.DAP.MNG\Sources\AF.WS.ProjetEtDevis.DAP.MNG.BusinessLogic\AuthorizationRequestMngService.cs:ligne 134 à AF.WS.ProjetEtDevis.DAP.MNG.BusinessLogic.AuthorizationRequestMngService.GenerateEdaMessage(String topicName, AuthorizationRequest request) dans C:\src\Git\AF.WS.ProjetEtDevis.DAP.MNG\Sources\AF.WS.ProjetEtDevis.DAP.MNG.BusinessLogic\AuthorizationRequestMngService.cs:ligne 124 à AF.WS.ProjetEtDevis.DAP.MNG.UnitTests._2._BusinessLogic_Tests.AuthorizationRequestMngServiceUnitTest.ShouldSerializeAuthorizationRequest_WhenCorrectDataSent() dans C:\src\Git\AF.WS.ProjetEtDevis.DAP.MNG\Sources\AF.WS.ProjetEtDevis.DAP.MNG.UnitTests. BusinessLogic Tests\AuthorizationRequestMngServiceUnitTest.cs:ligne 251 InnerException:

英文消息是:

Type 'AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities.AuthorizationRequest' with data contract name 'AuthorizationRequest:http://schemas.datacontract.org/2004/07/AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.

请注意,我不能使用除 DataContractJsonSerializer

以外的其他序列化程序

您需要使用 known type mechanism. From the docs:

预先通知 DataContractJsonSerializer object Payload 成员中可能出现的类型

Polymorphism

Polymorphic serialization consists of the ability to serialize a derived type where its base type is expected. This is supported for JSON serialization by WCF comparable to the way XML serialization is supported. For example, you can serialize MyDerivedType where MyBaseType is expected, or serialize Int where Object is expected...

Preserving Type Information

As stated earlier, polymorphism is supported in JSON with some limitations...

To preserve type identity, when serializing complex types to JSON a "type hint" can be added, and the deserializer recognizes the hint and acts appropriately. The "type hint" is a JSON key/value pair with the key name of "__type" (two underscores followed by the word "type"). The value is a JSON string of the form "DataContractName:DataContractNamespace" (anything up to the first colon is the name).

最常见的方法是将 KnownTypeAttribute 应用于相关数据契约对象本身:

[DataContract]
[KnownType(typeof(AuthorizationRequest))]
public class Message
{
    [DataMember(Name = "technical", Order = 1)]
    public Technical Technical;

    [DataMember(Name = "payload", Order = 2)]
    public object Payload;
}

你也可以使用DataContractJsonSerializer(Type, IEnumerable<Type>)构造函数:

var ser = new DataContractJsonSerializer(typeof(Message), new [] { typeof(AuthorizationRequest)});

这样做之后,您的序列化 JSON 将如下所示,其中包含用于指定有效负载类型的多态类型提示 __type

{
  "technical": {
    "topic": "my topic",
    "nature": "my nature",
    "event_id": "1010101"
  },
  "payload": {
    "__type": "AuthorizationRequest:#Question48583688"
  }
}

如果您需要更灵活地指定已知类型,请参阅 Configuring Known Types Dynamically – Introducing the DataContractResolver. But be sure not to allow any and all types to be resolved. If you do, you will introduce security problems into your app like those discussed in