DataContractJsonSerializer с членом типа объекта

У меня есть следующий метод сериализации:

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 объект, имеющий DataContract а также DataMember тоже. Вот полная трассировка стека исключений:

L'exception System.Runtime.Serialization.SerializationException без дополнительной информации об использовании кода HResult=-2146233076
Сообщение = тип файла "AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities.AuthorizationRequest" запрос подтверждения доступа "AuthorizationRequest: http://schemas.datacontract.org/2004/07/AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities'n'est pas Participu. Использование данных DataContractResolver с использованием данных DataContractSerializer с различными типами данных, не зависящими от типа connus, в частности, с точки зрения использования Известные типы атрибутов, которые вы можете использовать в качестве альтернативы, могут быть изменены.
Источник =System.Runtime.Serialization StackTrace: à System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract DataContract, XmlWriterDelegator XMLWriter, OBJ Object, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Тип declaredType) à System.Runtime.Serialization.Json.XmlObjectSerializerWriteContextComplexJson.SerializeWithXsiType(XmlWriterDelegator XMLWriter, OBJ Объект, RuntimeTypeHandle objectTypeHandle, Тип ObjectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Тип declaredType) меню System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator XMLWriter, OBJ Object, Boolean, Boolean isDeclaredType writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) à System.Runtime.Serialization.XmlObjectSerializerWriteContextComplex.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, логический isDeclaredType, логический writeXsiType, объявленный Int32TypeID, RuntimeTyp eHandle declaredTypeHandle) меню WriteMessageToJson(XmlWriterDelegator, объект, XmlObjectSerializerWriteContextComplexJson, ClassDataContract, XmlDictionaryString[]) меню System.Runtime.Serialization.Json.JsonClassDataContract.WriteJsonValueCore(XmlWriterDelegator jsonWriter, OBJ Object, XmlObjectSerializerWriteContextComplexJson контексте, RuntimeTypeHandle declaredTypeHandle) а System.Runtime.Serialization.Json.XmlObjectSerializerWriteContextComplexJson.WriteDataContractValue(DataContract DataContract, XmlWriterDelegator XMLWriter, OBJ Объект, RuntimeTypeHandle declaredTypeHandle) меню System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithoutXsiType(DataContract DataContract, XmlWriterDelegator XMLWriter, OBJ Объект, RuntimeTypeHandle declaredTypeHandle) а System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObjectContent(модуль записи XmlWriterDelegator, граф объектов) à System.Runtime.Serialization.Json.DataContractJsonSerializer.InternalWriteObject(Писатель XmlWriterDelegator, граф объектов) Runtime.Serialization.Json.DataContractJsonSerializer.WriteObject(модуль записи XmlDictionaryWriter, граф объектов). Сериализация (сообщение сообщения) dans C:\src\Git\AF.WS.ProjetEtDevis.DAP.MNG\Sources\AF.WS.ProjetEtDevis.DAP.MNG.BusinessLogic\AuthorizationRequestMngService.cs:ligne 134 à AF.WS.ProjetED.DAP.MNG.BusinessLogic.AuthorizationRequestMngService.GenerateEdaMessage(String topicName, запрос AuthorizationRequest) dans C:\src\Git\AF.WS.ProjetEtDevis.DAP.MNG\Sources\AF.WS.: \ SRC \Git\AF.WS.ProjetEtDevis.DAP.MNG\Sources\AF.WS.ProjetEtDevis.DAP.MNG.UnitTests\2. BusinessLogic Tests\AuthorizationRequestMngServiceUnitTest.cs:ligne 251 InnerException:

На английском это сообщение:

Введите "AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities.AuthorizationRequest" с именем контракта данных "AuthorizationRequest: http://schemas.datacontract.org/2004/07/AF.WS.ProjetEtDevis.DAP.MNG.BusinessEntities" неожиданно. Добавьте любые типы, которые не известны статически, в список известных типов - например, с помощью атрибута KnownTypeAttribute или добавив их в список известных типов, переданных DataContractSerializer.

Обратите внимание, что я не могу использовать другой сериализатор, кроме DataContractJsonSerializer

1 ответ

Решение

Вам необходимо сообщить DataContractJsonSerializer заранее возможных типов, которые могут возникнуть в object Payload член с помощью механизма известного типа. Из документов:

Полиморфизм

Полиморфная сериализация состоит из возможности сериализации производного типа там, где ожидается его базовый тип. Это поддерживается для сериализации JSON WCF, сравнимой с тем, как поддерживается сериализация XML. Например, вы можете сериализовать MyDerivedType, где ожидается MyBaseType, или сериализовать Int, где ожидается Object...

Сохранение информации о типе

Как говорилось ранее, полиморфизм поддерживается в JSON с некоторыми ограничениями...

Чтобы сохранить идентичность типов, при сериализации сложных типов в JSON можно добавить "подсказку типа", а десериализатор распознает подсказку и будет действовать соответствующим образом. "Подсказка типа" - это пара ключ / значение JSON с именем ключа "__type" (два подчеркивания, за которыми следует слово "type"). Значение представляет собой строку JSON в форме "DataContractName:DataContractNamespace" (имя до любого двоеточия является первым).

Самый распространенный способ сделать это - применить 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"
  }
}

Если вам нужно больше гибкости в том, как указываются известные типы, см. Динамическая настройка известных типов - Представление DataContractResolver. Но будьте уверены, чтобы не разрешить все типы. Если вы это сделаете, вы добавите в свое приложение проблемы безопасности, подобные тем, которые обсуждались в разделе "Предупреждение TypeNameHandling в Newtonsoft Json".

Другие вопросы по тегам