Меню

Ошибка сериализации что это

LordXaosa, C# позволяет управлять процессом сериализации/десериализации.
Например, класс содержит вычисляемые поля и в целях экономии места нету смысла эти поля так же сохранять в файл — эти поля можно пометить атрибутом игнорирования (у каждого форматера свой, у BinaryFormatter — [NonSerialized]). А при восстановлении эти поля можно просто вычислить единожды для каждого экземпляра снова.
Для такой задачи достаточно унаследовать классом интерфейс IDeserializationCallback и определить его функцию

C#
1
public void OnDeserialization(object sender)

В момент, когда эта функция получает управление, все значимые поля уже содержат значения из файла и к ним можно спокойно обращаться. Пример использования этого интерфейса я дал в своем прошлом проекте .

Для задач, когда нужно перед записью в файл изменить значения некоторых значений (например зашифровать), существует другой интерфейс —

ISerializable и две его функции:

C#
1
2
3
public virtual void GetObjectData(SerializationInfo info,  StreamingContext context) {}
// The following constructor is for deserialization
protected ShoppingCartItem(SerializationInfo info,  StreamingContext context) {}

Вот маленький шаблонный пример как ими воспользоваться:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using System.IO;
 
namespace CustomSerialization
{
  class Program
  {
    [Serializable]
    public class SomeClass:ISerializable
    {
      [NonSerialized]
      public string secret1,secret2;
      public string open1,open2;
 
      public SomeClass(string secret1,string secret2,string open1,string open2)
      {
        this.secret1=secret1;
        this.secret2=secret2;
        this.open1=open1;
        this.open2=open2;
      }
 
      private string CryptF(string s)
      {
        //тут можно вызвать какой то метод шифрования
        //для простоты я возвращу туже строку
        return s;
      }
 
      private string DecryptF(string s)
      {
        //тут можно вызвать какой то метод дешифрования
        //для простоты я возвращу туже строку
        return s;
      }
 
      #region ISerializable Members
        private SomeClass(SerializationInfo info,StreamingContext context)
        {
          //считываем все поля, но те которые нужно, предварительно "расшифровываем"
          secret1=DecryptF(info.GetString("Some crypted secret1"));
          secret2=DecryptF(info.GetString("Some crypted secret2"));
          open1=info.GetString("public1");
          open2=info.GetString("public2");
        }
 
        public void GetObjectData(SerializationInfo info,StreamingContext context)
        {
          //добавлять нужно все поля, но те которые нужно, передаются в "зашифрованном" виде
          //в качестве идентификаторов можно использовать любой текст
          info.AddValue("Some crypted secret1",CryptF(secret1));
          info.AddValue("Some crypted secret2",CryptF(secret2));
          info.AddValue("public1",open1);
          info.AddValue("public2",open2);
        }
      #endregion
    }
 
    static void Main(string[] args)
    {
      SomeClass a=new SomeClass("top secret1","top secret2","some public text1",
        "some public text2");
      
      //serialization
      BinaryFormatter bf=new BinaryFormatter();
      FileStream fs=new FileStream("data.bin",FileMode.Create);
      bf.Serialize(fs,a);
      fs.Close();
 
      //deserialization
      fs=new FileStream("data.bin",FileMode.Open);
      SomeClass b=(SomeClass)bf.Deserialize(fs);
 
      Console.WriteLine("{0}; {1}; {2}; {3}",b.secret1,b.secret2,b.open1,b.open2);
      Console.ReadKey();
    }
  }
}

Для класса BinaryFormatter есть еще возможность назначить специальными атрибутами функции обработчики событий начала/окончания сериализации/десериализации…

Вообще-то тут можно много говорить по поводу способов управления процессом сериализации/десериализации, но зачем повторять то, что давно уже написано ?
Можно покопать по этому поводу MSDN или вот [] книгу, раздел 5, глава 3 (а можно и первые 2 тоже ). Я изучал эту тему именно по этой книге.

С классом CryptoStream не сталкивался, ничего подсказать не могу. Может завтра поковыряюсь с ним.



1



Сериализация — это автоматический процесс преобразования структур данных или состояний объектов в формат, который Unity может сохранять и реконструировать позже. (Дополнительную информацию см. в документации по сериализации сценариев.)

В некоторых случаях сериализация скрипта может вызвать ошибки. Исправления некоторых из них перечислены ниже.

Вызов Unity Scripting API из конструктора или инициализаторов полей

Вызов Scripting API, например GameObject.Find внутри MonoBehaviour конструктор или инициализатор поля вызывает ошибку: «Find нельзя вызывать из конструктора MonoBehaviour (или инициализатора поля экземпляра), вместо этого вызовите его в Awake или Start».

Исправьте это, выполнив вызов Scripting API в MonoBehaviour.Start, а не в конструкторе.

Вызов Unity Scripting API во время десериализации

Вызов Scripting API, такого как GameObject.Find, из конструктора класса, отмеченного System.Serializable вызывает ошибку: «Find нельзя вызывать во время сериализации, вместо этого вызовите его из Awake или Start».

Чтобы исправить это, отредактируйте свой код, чтобы он не вызывал вызовы Scripting API в любых конструкторах для любых сериализованных объектов.

Потокобезопасный API скриптов Unity

На большую часть Scripting API распространяются перечисленные выше ограничения. Исключением являются только некоторые части API сценариев Unity, которые можно вызывать из любого места. Это:

  • Debug.Log

  • Mathf функции

  • Простые автономные структуры; например, математические структуры, такие как Vector3 и QuaternionСтандартный способ Unity для представления поворотов в виде данных. При написании кода, имеющего дело с поворотами, обычно следует использовать класс Quaternion и его методы. Подробнее
    См. в Словарь

Чтобы снизить риск ошибок во время сериализации, вызывайте только автономные методы API, которым не нужно получать или устанавливать данные в самом Unity. Вызывайте их только в том случае, если нет альтернативы.


Offline

Gwalf

 


#1
Оставлено
:

3 апреля 2018 г. 15:25:38(UTC)

Gwalf

Статус: Активный участник

Группы: Участники

Зарегистрирован: 17.01.2018(UTC)
Сообщений: 38
Российская Федерация

Сказал(а) «Спасибо»: 1 раз

Нет доступа к ЦР.

Ping-CA выдал ошибку сериализации: CA error.jpg (75kb) загружен 49 раз(а).

С чем связана данная ошибка?
Как восстановить работу УЦ?


Вверх


Вверх
 

Offline

Захар Тихонов

 


#2
Оставлено
:

3 апреля 2018 г. 15:29:48(UTC)

Захар Тихонов

Статус: Сотрудник

Группы: Участники

Зарегистрирован: 17.08.2015(UTC)
Сообщений: 3,063
Мужчина
Тонга
Откуда: Калининград

Сказал «Спасибо»: 35 раз
Поблагодарили: 548 раз в 525 постах

Выполните команду на ЦР
Get-CAReference
и приложите вывод.

Также пришлите вывод команды с сервера ЦС
(Get-Item ‘CA:<имя ЦС>’).Subject


Вверх


Offline

Gwalf

 


#3
Оставлено
:

3 апреля 2018 г. 15:33:08(UTC)

Gwalf

Статус: Активный участник

Группы: Участники

Зарегистрирован: 17.01.2018(UTC)
Сообщений: 38
Российская Федерация

Сказал(а) «Спасибо»: 1 раз

ЦР и ЦС развернуты в единственном экземпляре на одной машине.
Сертификаты не просрочены.


Вверх


Offline

Захар Тихонов

 


#4
Оставлено
:

3 апреля 2018 г. 15:42:41(UTC)

Захар Тихонов

Статус: Сотрудник

Группы: Участники

Зарегистрирован: 17.08.2015(UTC)
Сообщений: 3,063
Мужчина
Тонга
Откуда: Калининград

Сказал «Спасибо»: 35 раз
Поблагодарили: 548 раз в 525 постах

Ок, еще раз:
Выполните команду на ЦР
Get-CAReference
и приложите вывод.

Также пришлите вывод команды с сервера ЦС
(Get-Item ‘CA:<имя ЦС>’).Subject

Если это один сервер, то это не отменяет того, что нужно выполнить команды и прислать выводы.


Вверх


Offline

Gwalf

 


#5
Оставлено
:

3 апреля 2018 г. 16:06:22(UTC)

Gwalf

Статус: Активный участник

Группы: Участники

Зарегистрирован: 17.01.2018(UTC)
Сообщений: 38
Российская Федерация

Сказал(а) «Спасибо»: 1 раз

Get-CAReference: Get-CAReference.jpg (122kb) загружен 40 раз(а).

Где можно проверить имя ЦС?

Отредактировано пользователем 3 апреля 2018 г. 16:17:07(UTC)
 | Причина: Не указана


Вверх


Offline

Захар Тихонов

 


#6
Оставлено
:

3 апреля 2018 г. 16:18:09(UTC)

Захар Тихонов

Статус: Сотрудник

Группы: Участники

Зарегистрирован: 17.08.2015(UTC)
Сообщений: 3,063
Мужчина
Тонга
Откуда: Калининград

Сказал «Спасибо»: 35 раз
Поблагодарили: 548 раз в 525 постах

Посмотрите его в Диспетчере УЦ.


Вверх


Offline

Gwalf

 


#7
Оставлено
:

3 апреля 2018 г. 16:39:44(UTC)

Gwalf

Статус: Активный участник

Группы: Участники

Зарегистрирован: 17.01.2018(UTC)
Сообщений: 38
Российская Федерация

Сказал(а) «Спасибо»: 1 раз

Subject.jpg (30kb) загружен 34 раз(а).


Вверх


Offline

Захар Тихонов

 


#8
Оставлено
:

3 апреля 2018 г. 16:43:10(UTC)

Захар Тихонов

Статус: Сотрудник

Группы: Участники

Зарегистрирован: 17.08.2015(UTC)
Сообщений: 3,063
Мужчина
Тонга
Откуда: Калининград

Сказал «Спасибо»: 35 раз
Поблагодарили: 548 раз в 525 постах

Возможно остановлена служба SQL. В Диспетчере конфигурации SQL, ваш экземпляр SQL запущен?


Вверх


Offline

Gwalf

 


#9
Оставлено
:

3 апреля 2018 г. 17:00:12(UTC)

Gwalf

Статус: Активный участник

Группы: Участники

Зарегистрирован: 17.01.2018(UTC)
Сообщений: 38
Российская Федерация

Сказал(а) «Спасибо»: 1 раз

Служба SQL запущена.

В логах по-кругу идут 4 ошибки:

1.
Службе регистрации Крипто-Про УЦ 2.0 не удалось обновить CDP с УЦ ПЭС.(Код ошибки 768)

2.
При синхронизации количества пользователей с ЦС УЦ ПЭС получено исключение.
System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: Закрытый ключ сертификата, которым подписан запрос, недействителен. (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.ArgumentException: Закрытый ключ сертификата, которым подписан запрос, недействителен.
at CertificateService.BusinessLogic.CertScriptContext.VerifyCertificate(X509Certificate2 certificate)
at CertificateService.ServiceImplementation.CertServiceAuthorizationManager.CheckAccess(OperationContext operationContext, Message& message)
at System.ServiceModel.Dispatcher.AuthorizationBehavior.Authorize(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)).

3.
Ошибка при обращении к ЦС. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: Закрытый ключ сертификата, которым подписан запрос, недействителен.

Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at CertificateService.ServiceContracts.ICertRequestContract.GetPolicy(String authority)
— End of inner exception stack trace —
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at RegistrationService.RegSrv.TransmitQueueService.OnMessage(IScriptContext`1 context, Message message)
HResult = -2146232828 (0x80131604)
=== InnerException #2 ===
System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: Закрытый ключ сертификата, которым подписан запрос, недействителен. (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.ArgumentException: Закрытый ключ сертификата, которым подписан запрос, недействителен.
at CertificateService.BusinessLogic.CertScriptContext.VerifyCertificate(X509Certificate2 certificate)
at CertificateService.ServiceImplementation.CertServiceAuthorizationManager.CheckAccess(OperationContext operationContext, Message& message)
at System.ServiceModel.Dispatcher.AuthorizationBehavior.Authorize(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)).
HResult = -2146233087 (0x80131501).

4.
При запросе политики с ЦС получено исключение.
System.Runtime.Serialization.SerializationException: Во время сериализации произошла ошибка. —> System.Runtime.Serialization.SerializationException: Type ‘System.ServiceModel.ExceptionDetail’ in Assembly ‘System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ is not marked as serializable.
at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
at RegistrationService.BusinessLogic.MethodCall.SetThrownException(Exception thrownException)
at RegistrationService.RegSrv.TransmitQueueService.OnMessage(IScriptContext`1 context, Message message)
— End of inner exception stack trace —
at RegistrationService.BusinessLogic.ProcessClient.Wait(Queue queue, Conversation conversation, TimeSpan timeout)
at RegistrationService.BusinessLogic.ProcessClient.WaitComplete(TimeSpan timeout)
at RegistrationService.RegSrv.PolicyUpdateTask.Execute(IScriptContext`1 context)


Вверх


Offline

Захар Тихонов

 


#10
Оставлено
:

3 апреля 2018 г. 17:02:45(UTC)

Захар Тихонов

Статус: Сотрудник

Группы: Участники

Зарегистрирован: 17.08.2015(UTC)
Сообщений: 3,063
Мужчина
Тонга
Откуда: Калининград

Сказал «Спасибо»: 35 раз
Поблагодарили: 548 раз в 525 постах

при выполнении команды Get-CAReference указан клиентский сертификат ЦР. Там указаны сроки действия сертификата. Откройте этот сертификат, найдите расширение срок действия ЗК. Если он истек, то выполните смену клиентского сертификата ЦР.


Вверх

Пользователи, просматривающие эту тему

Guest

Быстрый переход
 

Вы не можете создавать новые темы в этом форуме.

Вы не можете отвечать в этом форуме.

Вы не можете удалять Ваши сообщения в этом форуме.

Вы не можете редактировать Ваши сообщения в этом форуме.

Вы не можете создавать опросы в этом форуме.

Вы не можете голосовать в этом форуме.

Содержание

  • Механизмы сериализации
  • Сериализатор контрактов данных
    • Сериализация производных типов
    • Объектные ссылки
    • Совместимость версий
    • Упорядочение членов
    • Пустые значения и null
    • Сериализация коллекций
    • Хуки сериализации
    • Поддержка механизмов двоичной сериализации
  • Двоичный сериализатор
    • Атрибут [Serializable]
    • Выполнение двоичной сериализации
    • Атрибут [NonSerialized]
    • Атрибут [OptionalField]
    • Интерфейс ISerializable
  • XML сериализатор
    • Выполнение XML сериализации
    • Обработка производных классов и объектных ссылок
    • Сериализация коллекций
    • IXmlSerializable

Сериализация — процесс преобразования объекта или группы связанных объектов в поток байт или набор XML узлов для сохранения (в базу данных, файл и т.д.) или передачи (например, по сети). Десериализация — обратный процесс — восстановление объекта из потока байт или XML узлов. Сериализация обычно используется для передачи объектов по сети или за границы приложения, для сохранения объектов внутри файлов или базы данных, а также для глубокого копирования объектов.

Классы для сериализации располагаются в двух пространствах имен: System.Runtime.Serialization и System.Xml.Serialization.

Механизмы сериализации

Для сериализации в .NET доступно 4 механизма:

  • сериализатор контрактов данных
  • двоичный сериализатор
  • XML сериализатор (XmlSerializer)
  • интерфейс IXmlSerializable

Первые три являются готовыми механизмами для сериализации и выполняют всю работу самостоятельно, интерфейс IXmlSerializable предполагает реализацию механизма сериализации самостоятельно.

Наличие трех разных механизмов сериализации является исторически сложившимся.

Сериализатор контрактов данных является самым новым и универсальным. Он может быть использован при обмене информацией через протоколы обмена сообщениями. Он также обеспечивает хорошую переносимость версий и дает возможность десериализовать данные полученные из более ранней или более поздней версии типа, за счет того что устраняет связь между низкоуровневыми деталями объектов и структурой сериализованных данных. Сериализатор контрактов данных может быть использован в большинстве задач вместо двоичного и XML сериализатора.

Двоичный сериализатор прост в применении, хорошо автоматизирован и поддерживается повсеместно в .NET. Двоичная сериализация используется инфраструктурой Remoting, в т.ч. при взаимодействии между двумя доменами приложений в одном и том же процессе. Двоичный сериализатор работает быстрее чем сериализатор контрактов данных, но так дает худшую переносимость, поскольку тесно связывает внутреннюю структуру типа с форматом сериализованных данных. Он также не может быть использован для получения XML.

XmlSerializer может генерировать только XML, и по сравнению с другими механизмами он менее функционален при сериализации сложных групп объектов. Однако во взаимодействии с XML он дает наибольшую функциональность, а также обеспечивает хорошую переносимость версий.

Реализация интерфейса IXmlSerializable предполагает самостоятельное выполнение сериализации с помощью XmlReader и XmlWriter.

Вывод сериализатора контрактов данных и двоичного сериализатора оформляется с помощью подключаемого форматера. Форматер приводит форму финального представления в соответствие с конкретной средой или контекстом сериализации. Доступно два форматера: форматер XML и двоичный форматер. XML форматер используется в контексет чтения/записи XML, тектовых файлов и потоков, обмена сообщениями SOAP. Двоичный форматер используется в контексте произвольного потока байт. Двоичный вывод по размерам обычно меньше чем XML, иногда значительно. Теоретически механизм сериалзиции не связан с форматером, но на практике сериализатор контрактов данных использует XML форматер, а двоичный сериализатор — двоичный форматер.

Сериализатор контрактов данных

Использование сериализатора контрактов данных предполагает следующие три шага

  • выбрать класс для использования: DataContractSerializerили NetDataContractSerializer
  • добавить сериализуемым типам и членам атрибуты [DataContract]и [DataMember] (соответственно)
  • создать экземпляр сериализатора и вызвать его метод WriteObjectили ReadObject

Существует два сериализатора контрактов данных:

  • DataContractSerializer — обеспечивает слабую привязку типов .NET к типам контрактов данных. Может генерировать совместимый со стандартами XML код. Требует предварительной явной регистрации сериализуемых производных типов, чтобы иметь возможность сопоставлять имена контрактов данных с именами типов .NET
  • NetDataContractSerializer — характеризуется тесной привязкой типов .NET к типам контрактов данных, не требует явной регистрации сериализуемых производных типов, т.к. самостоятельно записывает полные имена типов и сборок сериализуемых типов

После выбора сериализатора необходимо всем сериализуемым типам добавить атрибут[DataContract], а их членам, которые необходимо включить в сериализацию, — атрибуты [DataMember]:

namespace SerialTest

{

  [DataContract] public class Person

  {

      [DataMember] public string Name;

      [DataMember] public int Age;

  }

}

После этого можно явно сериализовать и десериализовать объекты, создавая экземпляр DataContractSerializer или NetDataContractSerializer и вызывая метод WriteObject или ReadObject:

Person p = new Person { Name = «Stacey», Age = 30 };

var ds = new DataContractSerializer (typeof (Person));

using (Stream s = File.Create («person.xml»))

  ds.WriteObject (s, p); // Сериализация

Person p2;

using (Stream s = File.OpenRead («person.xml»))

  p2 = (Person) ds.ReadObject (s); // Десериализация

Console.WriteLine (p2.Name + » « + p2.Age); // Stacey 30

Конструктору DataContractSerializer необходимо передать тип корневого объекта — сериализуемый объект, который в XML дереве будет корневым элементом. Конструктор NetDataContractSerializer этого не требует. В остальном их применение аналогично.

Оба типа сериализаторов по умолчанию принимаю форматер XML, поэтому с ними можно использовать XmlWriter:

Person p = new Person { Name = «Stacey», Age = 30 };

var ds = new DataContractSerializer (typeof (Person));

// Добавить в вывод отступы:

XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };

using (XmlWriter w = XmlWriter.Create («person.xml», settings))

ds.WriteObject (w, p);

System.Diagnostics.Process.Start («person.xml»);

Имена XML элементов в выводе соответствуют именам контрактов данных, которые по умолчанию совпадают с именами типов в .NET, однако это можно переопределить с помощью атрибутов, задав альтернативное имя для элемента:

[DataContract (Name=«Candidate»)]

public class Person { ... }

Также можно изменить пространство имен по умолчанию для корневого элемента:

[DataContract (Namespace=«http://somedomain.com/contacts»)]

public class Person { ... }

Можно также переопределить имена членов данных:

[DataContract (Name=«Candidate», Namespace=«http://oreilly.com/nutshell»)]

public class Person

{

  [DataMember (Name=«FirstName»)] public string Name;

  [DataMember (Name=«ClaimedAge»)] public int Age;

}

Атрибут [DataMember] может быть применен к частным и публичным полям и свойствам следующих типов данных:

  • любой примитивный тип
  • DateTime, TimeSpan, Guid, Uri или Enum
  • типы, допускающие значение null вышеуказанных типов
  • byte[] (сериализуется в XML с применением кодировки Base64
  • любой тип с атрибутом [DataContract]
  • любой тип IEnumerable
  • любой тип с атрибутом [Serializable] или реализующий интерфейс ISerializable
  • любой тип реализующий интерфейс IXmlSerializable

Можно использовать двоичный форматер:

Person p = new Person { Name = «Stacey», Age = 30 };

var ds = new DataContractSerializer (typeof (Person));

var s = new MemoryStream();

using (XmlDictionaryWriter w = XmlDictionaryWriter.CreateBinaryWriter (s))

  ds.WriteObject (w, p);

var s2 = new MemoryStream (s.ToArray());

Person p2;

using (XmlDictionaryReader r = XmlDictionaryReader.CreateBinaryReader (s2, XmlDictionaryReaderQuotas.Max))

  p2 = (Person) ds.ReadObject (r);

Сериализация производных типов

При сериализации производных типов если используется NetDataContractSerializer никаких дополнительных действий не требуется, необходимо только снабдить производный класс атрибутом[DataContract]. Сериализатор будет записывать полностью определенные имена типов:

<Person ...

z:Type=«SerialTest.Person»

z:Assembly=«SerialTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null»>

В случае с DataContractSerializer сериализатору необходимо сообщить обо всех производных типах, которые он может сериализовать. Если этого не сделать при десерриализации возникнет ошибка, т.к. сериализатор не сможет узнать в какой именно тип следует преобразовать элемент:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

// Базовый класс:

[DataContract] public class Person

{

  [DataMember] public string Name;

  [DataMember] public int Age;

}

// Производные классы:

[DataContract] public class Student : Person { }

[DataContract] public class Teacher : Person { }

// Метод выполняющий глубокое клонирование с применением сериализации:

static Person DeepClone (Person p)

{

  var ds = new DataContractSerializer (typeof (Person));

  MemoryStream stream = new MemoryStream();

  ds.WriteObject (stream, p);

  stream.Position = 0;

  return (Person) ds.ReadObject (stream);

}

// Применение метода:

Person person = new Person { Name = «Stacey», Age = 30 };

Student student = new Student { Name = «Stacey», Age = 30 };

Teacher teacher = new Teacher { Name = «Stacey», Age = 30 };

Person p2 = DeepClone (person); // OK

Student s2 = (Student) DeepClone (student); // SerializationException

Teacher t2 = (Teacher) DeepClone (teacher); // SerializationException

Сообщить сериализатору о производных типах можно при создании экземпляра DataContractSerializer, либо в самом типе с помощью атрибута[KnownType]:

var ds = new DataContractSerializer (typeof (Person),

  new Type[] { typeof (Student), typeof (Teacher) } );

[DataContract, KnownType (typeof (Student)), KnownType (typeof (Teacher))]

public class Person

Сериализованный объект Student будет выглядеть так:

<Person xmlns=«…»

  xmlns:i=«http://www.w3.org/2001/XMLSchema-instance»

  i:type=«Student» >

    …

<Person>

Поскольку в качестве корневого типа указан Person, корневой элемент имеет его имя, а действительный подкласс описан отдельно в атрибуте type.

Объектные ссылки

Ссылки на другие объекты также сериализуются. Напрмер:

[DataContract] public class Person

{

  [DataMember] public string Name;

  [DataMember] public int Age;

  [DataMember] public Address HomeAddress;

}

[DataContract] public class Address

{

  [DataMember] public string Street, Postcode;

}

Будет сериализовано в:

<Person…>

    <Age></Age>

    <HomeAddress>

        <Street></Street>

        <Postcode></Postcode>

    </HomeAddress>

    <Name></Name>

</Person>

Класс NetDataContractSerializer всегда сохраняет эквивалентность ссылок на объекты. Класс DataContractSerializer по умолчанию этого не делает. Это означает, что если на один и тот же объект имеются ссылки в двух разных местах, DataContractSerializer запишет его дважды. Например, для такого объекта:

[DataContract] public class Person

{

  [DataMember] public Address HomeAddress, WorkAddress;

}

Person p = new Person { Name = «Stacey», Age = 30 };

p.HomeAddress = new Address { Street = «Odo St», Postcode = «6020» };

p.WorkAddress = p.HomeAddress;

XML будет содержать тот же самый адрес дважды:

...

<HomeAddress>

    <Postcode>6020</Postcode>

    <Street>Odo St</Street>

</HomeAddress>

<WorkAddress>

    <Postcode>6020</Postcode>

    <Street>Odo St</Street>

</WorkAddress>

При последующей десериализации WorkAddress и HomeAddress будут преобразованы в два разных объекта. Преимуществом такого подходя является простота и совместимость XML, а недостатком — большой размер XML, потеря ссылочной целостности и невозможность справиться с циклическими ссылками.

Чтобы сохранить ссылочную эквивалентность, нужно в конструктор DataContractSerializer для аргумента preserveObjectReferen передать true:

var ds = new DataContractSerializer (typeof (Person),

  null, 1000, false, true, null);

При этом третьим аргументом необходимо передать максимальное количество объектных ссылок, которые сериализатор должен отслеживать (при привышении этого количество будет сгенерировано исключение). XML при сохранении ссылочной эквивалентности будет выглядеть так:

<Person xmlns=«http://schemas.datacontract.org/2004/07/SerialTest»

      xmlns:i=«http://www.w3.org/2001/XMLSchema-instance»

      xmlns:z=«http://schemas.microsoft.com/2003/10/Serialization/»

      z:Id=«1»>

    <Age>30</Age>

    <HomeAddress z:Id=«2»>

        <Postcode z:Id=«3»>6020</Postcode>

        <Street z:Id=«4»>Odo St</Street>

    </HomeAddress>

    <Name z:Id=«5»>Stacey</Name>

    <WorkAddress z:Ref=«2» i:nil=«true» />

</Person>

Совместимость версий

Можно добавлять и удалять члены в сериализуемые типы, не нарушая при этом прямой и обратной совместимости. Сериализаторы всегда пропускают при сериализации члены, для которых не установлен атрибут [DataMember], а при десериализации не генерируют исключений, если для члена, снабженного атрибутом [DataMember], нет сериализованных данных. Нераспознанные данные, присутствующие в потоке сериализации, но отсутствующие у десериализуемого типа, также молча пропускаются. Однако если тип реализует интерфейс IExtensibleDataObject, нераспознанные данные пропускаться не будут, а будут сохраняться в объект ExtensionDataObject, который может быть получен с помощью свойства ExtensionData:

[DataContract] public class Person : IExtensibleDataObject {

  [DataMember] public string Name;

  [DataMember] public int Age;

  ExtensionDataObject IExtensibleDataObject.ExtensionData { get; set; }

}

Если член является важным для объекта можно потребовать его присутствия с помощью IsRequired:

[DataMember (IsRequired=true)] public int ID;

Если такой член отсутствует, при десериализации будет сгенерировано исключение.

Упорядочение членов

Порядок следования членов при сериализации и десериализации играет определенное значение: при десериализации неправильно упорядоченные члены будут пропущены.

Упорядочение осуществляется при сериализации по следующим правилам:

  • сначала сериализуются члены базового класса, потом производного
  • для членов с установленным аргументом Order учитывается его значение (от меньшего к большему)
  • в последнюю очередь учитывается алфавитный порядок имен членов

Таким образом поле Age будет предшествовать полю Name, если только не поменять их порядок с помощью аргумента Order:

[DataContract] public class Person

{

  [DataMember (Order=0)] public string Name;

  [DataMember (Order=1)] public int Age;

}

Пустые значения и null

Члены типа, значениями которых является null или пустые значения, при сериализации по умолчанию все равно записываются с пустым значением:

<Person xmlns=«…»

      xmlns:i=«http://www.w3.org/2001/XMLSchema-instance»>

    <Name i:nil=«true» />

</Person>

Однако это не всегда целесообразно, поскольку ведет к бесполезному расходу пространства особенно для типов с множеством пустых полей и свойств. Поэтому сериализатору можно сообщить о необходимости пропускать пустые поля и свойства. Сделать это можно с помощью аргумента EmitDefaultValue:

[DataContract] public class Person

{

  [DataMember (EmitDefaultValue=false)] public string Name;

  [DataMember (EmitDefaultValue=false)] public int Age;

}

В результате Name будет пропущен, если его значение равно null, а Age будет пропущен, если его значение равно 0.

Сериализация коллекций

Сериализаторы контрактов данных могут сериализовать и десериализовать любую коллекцию:

[DataContract] public class Person

{

...

  [DataMember] public List<Address> Addresses;

}

[DataContract] public class Address

{

  [DataMember] public string Street, Postcode;

}

Вывод:

<Person ...>

    …

    <Addresses>

        <Address>

            <Postcode>6020</Postcode>

            <Street>Odo St</Street>

        </Address>

        <Address>

            <Postcode>6152</Postcode>

            <Street>Comer St</Street>

        </Address>

    </Addresses>

    …

</Person>

При этом сериализатор не кодирует информацию о конкретном типе коллекции. Это позволяет при десериализации изменить тип коллекции без генерации ошибок.

При сериализации кастомного типа коллекции можно настроить имя XML узла для каждого элемента коллекции. Для этого используется атрибут[CollectionDataContract]:

[CollectionDataContract (ItemName=«Residence»)]

public class AddressList : Collection<Address> { }

[DataContract] public class Person

{

  ...

  [DataMember] public AddressList Addresses;

}

Вывод будет таким:

...

  <Addresses>

        <Residence>

            <Postcode>6020</Postcode

          <Street>Odo St</Street>

        </Residence>

  ...

Атрибут [CollectionDataContract] также позволяет задать аргументы Namespace и Name. Аргумент Name не используется, когда коллекция сериализуется как свойство другого объекта, но применяется если коллекция сериализуется как корневой объект.

Атрибут [CollectionDataContract] может также использоваться для управления сериализацией словарей:

[CollectionDataContract (ItemName=«Entry»,

                       KeyName=«Kind»,

                       ValueName=«Number»)]

public class PhoneNumberList : Dictionary <string, string> { }

[DataContract] public class Person

{

  ...

  [DataMember] public PhoneNumberList PhoneNumbers;

}

Вывод:

...

  <PhoneNumbers>

        <Entry>

            <Kind>Home</Kind>

            <Number>08 1234 5678</Number>

        </Entry>

        <Entry>

            <Kind>Mobile</Kind>

            <Number>040 8765 4321</Number>

        </Entry>

    </PhoneNumbers>

Хуки сериализации

Непосредственно до и после сериализации или десерализации можно выполнить специальный метод. Задать такой метод можно с помощью следующих атрибутов:

  • [OnSerializing] — указывает метод для вызова перед сериализацией
  • [OnSerialized] — указывает метод для вызова после сериализации
  • [OnDeserializing] — указывает метод для вызова перед десериализацией
  • [OnDeserialized] — указывает метод для вызова после десериализации

Такой метод должен принимать единственный параметр, имеющий тип StreamingContext, но необходим он только для согласованности с механизмом двоичной сериализации и не используется сериализаторами контрактов данных.

Поскольку сериализаторы не выполняют конструкторы и инициализаторы при десериализации, данные методы могут использоваться как конструкторы или инициализаторы полей при десериализации, чтобы присвоить значения членам, исключенным из сериализации, поскольку без этого исключенные из сериализации члены получат при десериализации дефолтные значения для своих типов, а не те значения, которые им присваиваются в конструкторах или инициализаторах.

Методы могут быть как публичными так и частными. Производные типы могут определять свои хуки и они также будт вызваны.

Поддержка механизмов двоичной сериализации

Сериализаторы контрактов данных также может сериализовать типы помеченные атрибутом [Serializable] и реализующие интерфейс ISerializable. При этом не происходит переключения на двоичную сериализацию, но многие механизмы работают ка при двоичной сериализации: учитываются атрибуты [NonSerialized], выполняются методы GetObjectData и конструкторы десериализации.

Не допускается одновременно использовать атрибуты контрактов данных и атрибуты двоичной сериализации.

Двоичный сериализатор

Сделать тип поддерживающим двоичную сериализацию можно двумя путями:

  • добавить типу атрибут [Serializable]
  • реализовать в типе интерфейс ISerializable

Добавление атрибута проще, но реализация интерфейса дает больше возможностей.

Атрибут [Serializable]

Сделать тип сериализуемым можно с помощью единственного атрибута:

[Serializable] public sealed class Person

{

public string Name;

public int Age;

}

Атрибут [Serializable] указывает сериализатору на необходимость включать все поля данного типа, как публичные так и частные, но не включать свойства.

Каждое поле должно иметь сериализуемый тип, т.е. тип также помеченный атрибутом [Serializable], либо реализующий интерфейс ISerializable. Примитивные типы и множество других типов .NET являются сериализуемыми.

Атрибут Serializable не наследуется, поэтому производные классы не являются автоматически сериализуемыми, если их не пометить специально атрибутом Serializable.

Свойства типа не сериализуются, но сериализуются лежащие в основе их автоматически создаваемые поля (к сожалению эти поля могут переименовываться при перекомпиляции).

Выполнение двоичной сериализации

Чтобы выполнить двоичную сериализацию необходимо создать объект форматера и вызвать его метод Serialize. Для двоичной сериализации предусмотрено два форматера:

  • BinaryFormatter — более эффективный, генерирует небольшой вывод за меньшее время. Определен в пространстве имен System.Runtime.Serialization.Formatters.Binary
  • SoapFormatter — поддерживает базовый обмен сообщениями, менее функционален, не поддерживает сериализацию обобщенных типов и фильтрацию посторонних данных. Определен в пространстве имен System.Runtime.Serialization.Formatters.Soap

Оба форматера используются одинаково:

Person p = new Person() { Name = «George», Age = 25 };

IFormatter formatter = new BinaryFormatter();

using (FileStream s = File.Create («serialized.bin»))

  formatter.Serialize (s, p);

Метод Deserialize восстанавливает объект:

using (FileStream s = File.OpenRead («serialized.bin»))

{

  Person p2 = (Person) formatter.Deserialize (s);

  Console.WriteLine (p2.Name + » « + p.Age); // George 25

}

При воссоздании объектов десериализатор пропускает все конструкторы.

Сериализованные данные включают полные сведения о типе и сборке, поэтому если при десереиализации попытаться привести тип к совместимому типу, возникнет ошибка. Десериализатор полностью восстанавливает объектные ссылки в их исходное состояние.

Двоичные сериализаторы поддерживают также атрибуты [OnSerializing],[OnSerialized], [OnDeserializing] и [OnDeserialized]. Их применение не отличается от сериализаторов контрактов данных.

Атрибут [NonSerialized]

В отличие от сериализаторов контрактов данных, которые требую помечать атрибутами все сериализуемые поля, двоичный сериализатор этого не требует и по умолчанию включает все поля сериализуемого объекта. Исключить отдельные поля из сериализации можно отметив их атрибутом [NonSerialized]:

[Serializable] public sealed class Person

{

  public string Name;

  public DateTime DateOfBirth;

  [NonSerialized] public int Age;

}

Несериализованные члены при десериализации всегда получают пустое значение или null, даже если инициализаторы полей и конструкторы устанавливают их по другому.

Атрибут [OptionalField]

По умолчанию добавление нового поля нарушает совместимость с уже сериализованными данными и сериализатор выбросит исключение. Этого можно избежать добавив к новому полю атрибут [OptionalField]:

[Serializable] public sealed class Person

{

  public string Name;

  [OptionalField (VersionAdded = 2)] public DateTime DateOfBirth;

}

Это указывает сериализатору не генерировать исключение, если в потоке сериализованных данных он не встреит помеченного поля, а считать поле просто не сериализованным и оставить его пустым (ему можно затем присвоить значение с помощью метода [OnDeserializing]).

Аргумент VersionAdded — это целочисленное значение, инкрементируемое при каждом добавлении к типу новых полей. Оно носит скорее справочный характер и никак не влияет на сериализацию.

Вообще в целях сохранения версий рекомендуется избегать переименования, удаления и изменения типа полей.

Когда BinaryFormatter при десериализации обнаруживает в потоке сериализованных данных поле, которое не объявлено в типе, он его просто отбросит, аSoapFormatter сгененриует исключение.

Интерфейс ISerializable

Реализация интерфейса ISerializable предоставляет типу полный контроль над тем, как производится его двоичная сериализая и десериализация. Определение интерфейса выглядит следующим образом:

public interface ISerializable

{

  void GetObjectData (SerializationInfo info, StreamingContext context);

}

Метод GetObjectData запускается при сериализации и наполняет объект SerializationInfo (словарь пар имя-значение) данными из всех полей, подлежащих сериализации. Пример реализации для типа с двумя сериализуемыми полями Name и DateOfBirth:

public virtual void GetObjectData (SerializationInfo info,

StreamingContext context)

{

  info.AddValue («Name», Name);

  info.AddValue («DateOfBirth», DateOfBirth);

}

В примере ключи значений объекта SerializationInfo совпадают с именами полей в типе, но это не является обязательным: допустимо использовать любые строковые ключи, при условии, что конструктор десериализации сможет их преобразовать в имена полей. Сами значения могут относится к любому сериализуемому типу: при необходимости будет выполнена рекурсивная сериализация. Также допустимо если значением в словаре будет null.

Рекомендуется объявлять метод GetObjectData как virtual, чтобы производные классы могли расширять сериализацию не реализуя заново интрефейсISerializable.

Класс SerializationInfo также содержит свойства позволяющие управлять типом и сборкой, в которые должен быть помещен десериализованный экземпляр.

Параметр StreamingContext — это структрура, по мимо прочего содержащая значения enum, указывающие откуда поступает сериализованный экземпляр.

Помимо реализации интерфейса ISerializable, тип, управляющий собственной сериализацией должен содержать конструктор десериализации, принимающий те же два параметра, что и метод GetObjectData. Он может быть объявлен с любым модификатором доступа.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

[Serializable] public class Team : ISerializable

{

  public string Name;

  public List<Person> Players;

  public virtual void GetObjectData (SerializationInfo si, StreamingContext sc)

  {

      si.AddValue («Name», Name);

      si.AddValue («PlayerData», Players.ToArray());

  }

  public Team() {}

  // Конструктор десериализации

  protected Team (SerializationInfo si, StreamingContext sc)

  {

      Name = si.GetString («Name»);

      Person[] a = (Person[]) si.GetValue («PlayerData», typeof (Person[]));

      Players = new List<Person> (a);

  }

}

Для ряда типов класс SerializationInfo имеет специальные методы Get*, например, GetString, для более удобного получения значений по ключам. Если объект SerializationInfo не имеет значения с запрошенным ключом, будет сгенерировано исключение.

XML сериализатор

В пространстве имен System.Xml.Serialization определен еще один сериализатор — XmlSerializer. Он позволяет сериализовать типы в XML файлы. Как и при двоичной сериализации доступно два подхода:

  • добавлять к типу атрибуты из пространства имен System.Xml.Serialization
  • реализовывать интерфейс IXmlSerializable

В отличае от двоичной сериализации, реализация интерфейса IXmlSerializableполностью исключает применение встроенного сериализатора, оставляя разработчику самостоятельное написание кода сериализации с использованием XmlReaderи XmlWriter.

Выполнение XML сериализации

Для использования XmlSerializer необходимо создать его экземпляр и вызвать на нем Serialize или Deserialize, передав им в качестве аргументов поток (Stream), в который будут записываться сериализуемые данные (или из которого они будут читаться) и сериализуемый (или десериализуемый) объект:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// Сериализуемый тип:

public class Person

{

  public string Name;

  public int Age;

}

// Сериализуемый объект:

Person p = new Person();

p.Name = «Stacey»; p.Age = 30;

// Сериализация:

XmlSerializer xs = new XmlSerializer (typeof (Person));

using (Stream s = File.Create («person.xml»))

  xs.Serialize (s, p);

// Десериализация:

Person p2;

using (Stream s = File.OpenRead («person.xml»))

  p2 = (Person) xs.Deserialize (s);

Console.WriteLine (p2.Name + » « + p2.Age); // Stacey 30

Методы Serialize и Deserialize могут работать с Stream,XmlWriter/XmlReader или TextWriter/TextReader.

Для сериализации с помощью XmlSerializer добавлять сериализуемому типу какие-либо атрибуты не требуется. По умолчанию сериализуются все открытые поля и свойства типа. Исключить члены из сериализации можно с помощью атрибута [XmlIgnore]:

public class Person

{

  ...

  [XmlIgnore] public DateTime DateOfBirth;

}

XmlSerializer не распознает атрибут [OnDeserializing], а вместо него при десериализации использует конструктор без парарметров (в том числе неявно заданный) и генерирует исключение если он не найден. Также при десериализации выполняются инициализаторы полей:

public class Person

{

  public bool Valid = true; // Будет выполнено при десриализации

}

Следующие типы обрабатываются специальным образом:

  • примитивные типы, DateTime, TimeSpan, Guid и их версии, допускающие значение null вставляются как значения
  • byte[] — преобразуется в base 64
  • XmlAttribute и XmlElement — их контент непосредственно вставляется в XML (без обработки)
  • любой тип, реализующий IXmlSerializable — обрабатывается в соответствии с собственной реализацией
  • все типы коллекций

XmlSerializer обладает хорошей совместимостью: при десериализации он не жалуется если элементы или атрибуты отсутствуют либо встречаются лишние данные.

По умолчанию члены сериализуются в XML элементы. Если к члену добавить атрибут [XmlAttribute], он будет сериализован в XML атрибут:

[XmlAttribute] public int Age;

С помощью атрибутов можно изменять стандартные имена элементов и атрибутов:

public class Person

{

  [XmlElement («FirstName»)] public string Name;

  [XmlAttribute («RoughAge»)] public int Age;

}

Будет сериализовано:

<Person RoughAge=«30» ...>

    <FirstName>Stacey</FirstName>

</Person>

Стандартное пространство имен является пустым, задать его можно с помощью аргумента Namespace атрибутов [XmlElement] и [XmlAttribute]. Можно также назначить имя и пространство имен самому типу с помощью атрибута [XmlRoot]:

[XmlRoot («Candidate», Namespace = «http://mynamespace/test/»)]

public class Person { ... }

XmlSerializer записывает элементы в порядке, в котором они определены в классе. Изменить этот порядок можно с помощью аргумента Order элемента XmlElement:

public class Person

{

  [XmlElement (Order = 2)] public string Name;

  [XmlElement (Order = 1)] public int Age;

}

Если аргумент Order используется, он должен быть проставлен для всех элементов.

Обработка производных классов и объектных ссылок

Для того чтобы XmlSerializer мог корректно сериализовать и десериализовать производные классы, ему необходимо сообщить об их существовании. Сделать это можно двумя способами:

  • с помощью атрибута [XmlInclude], применяемого к базовому сериализуемому типу:

    [XmlInclude (typeof (Student))]

    [XmlInclude (typeof (Teacher))]

    public class Person { public string Name; }

  • указать производные типы при создании экземпляра XmlSerializer:

    XmlSerializer xs = new XmlSerializer (typeof (Person),

      new Type[] { typeof (Student), typeof (Teacher) } );

В обоих случаях сериализатор запишет производный тип в атрибут type:

<Person xmlns:xsi=«http://www.w3.org/2001/XMLSchema-instance»

      xsi:type=«Student»>

    <Name>Stacey</Name>

</Person>

При десериализации на основании значения этого атрибута десериализатор создаст объект нужного производного типа.

Именем, записываемым в XML атрибут type, можно управлять, применяя к производному типу атрибут [XmlType]:

[XmlType («Candidate»)]

public class Student : Person { }

XmlSerializer автоматически рекурсивно обрабатывает объектные ссылки:

public class Person

{

  public string Name;

  public Address HomeAddress = new Address();

}

public class Address { public string Street, PostCode; }

Person p = new Person(); p.Name = «Stacey»;

p.HomeAddress.Street = «Odo St»;

p.HomeAddress.PostCode = «6020»;

Будет сериализовано:

<Person ... >

    <Name>Stacey</Name>

    <HomeAddress>

        <Street>Odo St</Street>

        <PostCode>6020</PostCode>

    </HomeAddress>

</Person>

Если на один и тот же объект ссылаются два поля, объект будет сериализован дважды.

Если поле ссылается на объект производного типа, можно управлять тем, какое имя получит соответствующий XML элемент. Например, для следующей ситуации:

public class Address { public string Street, PostCode; }

public class USAddress : Address { }

public class AUAddress : Address { }

public class Person

{

  public string Name;

  public Address HomeAddress = new USAddress();

}

если зарегистрировать каждый производный класс, присвоив атрибуты [XmlInclude] базовому классу:

[XmlInclude (typeof (AUAddress))]

[XmlInclude (typeof (USAddress))]

public class Address

{

  public string Street, PostCode;

}

то имя XML элемента будет соответствовать имени ссылающегося поля или свойства, а реальный производный тип объекта будет записан в атрибут type:

<Person ...>

    …

    <HomeAddress xsi:type=«USAddress»>

        …

    </HomeAddress>

</Person>

Если к ссылающемуся полю или свойству применить несколько атрибутов [XmlElement]:

public class Person

{

  public string Name;

  [XmlElement («Address», typeof (Address))]

  [XmlElement («AUAddress», typeof (AUAddress))]

  [XmlElement («USAddress», typeof (USAddress))]

  public Address HomeAddress = new USAddress();

}

то каждый атрибут [XmlElement] сопоставит имя элемента с типом и имя XML элемента будет совпадать с именем производного типа:

<Person ...>

    …

    <USAddress>

        …

    </USAddress>

</Person>

При этом если в атрибуте [XmlElement] не указывать имя, а только тип, будет использовано стандартное имя типа (которое можно изменить с помощью [XmlType]).

Сериализация коллекций

XmlSerializer способен сериализовать коллекции без каких либо дополнительных настроек:

public class Person

{

  public string Name;

  public List<Address> Addresses = new List<Address>();

}

public class Address { public string Street, PostCode; }

Будет сериализовано в:

<Person ... >

    <Name></Name>

    <Addresses>

        <Address>

            <Street></Street>

            <Postcode></Postcode>

        </Address>

        <Address>

            <Street></Street>

            <Postcode></Postcode>

        </Address>

        …

    </Addresses>

</Person>

Атрибут [XmlArray] позволяет переименовать внешний элемент, а [XmlArrayItem] — внутренние элементы:

public class Person

{

  public string Name;

  [XmlArray («PreviousAddresses»)]

  [XmlArrayItem («Location»)]

  public List<Address> Addresses = new List<Address>();

}

Будет сериализовано в:

<Person ... >

    <Name></Name>

    <PreviousAddresses>

        <Location>

            <Street></Street>

            <Postcode></Postcode>

        </Location>

        <Location>

            <Street></Street>

            <Postcode></Postcode>

        </Location>

        …

    </PreviousAddresses>

</Person>

Оба атрибута также позволяют указывать пространство имен.

Чтобы сериализовать коллекцию без внешнего элемента, необходимо к ссылающемуся на коллекцию полю добавить атрибут [XmlElement] с аргументом имени, совпадающим с типом элементов коллекции:

public class Person

{

  ...

  [XmlElement («Address»)]

  public List<Address> Addresses = new List<Address>();

}

Если коллекция содержит элементы производных классов, то правил именования XML элементов следующие:

  • чтобы включить имя производного типа в атрибут type нужно добавить атрибут [XmlInclude] к базовому типу элементов коллекции (как это делалось выше):

    <Person ... >

        <Name></Name>

        <Addresses>

            <Address xsi:type=«AUAddress»>

  • если элементы коллекции должны именоваться в соответствии с их реальным типом (производным), необходимо применить несколько атрибутов [XmlArrayItem]или [XmlElement] к ссылающемуся на коллекцию полю:

    <Person ... >

        <Name></Name>

        <!-начало необязательного внешнего элемента->

        <AUAddress>

            <Street></Street>

            <Postcode></Postcode>

        </AUAddress>

        <USAddress>

            <Street></Street>

            <Postcode></Postcode>

        </USAddress>

        <!-конец необязательного внешнего элемента->

    </Person>

    При этом если использовать атрибут [XmlArrayItem], внешний элемент будет включен в XML:

    [XmlArrayItem («Address», typeof (Address))]

    [XmlArrayItem («AUAddress», typeof (AUAddress))]

    [XmlArrayItem («USAddress», typeof (USAddress))]

    public List<Address> Addresses = new List<Address>();

    А если использовать атрибут [XmlElement], внешний элемент будет исключен из коллекции:

    [XmlElement («Address», typeof (Address))]

    [XmlElement («AUAddress», typeof (AUAddress))]

    [XmlElement («USAddress», typeof (USAddress))]

    public List<Address> Addresses = new List<Address>();

IXmlSerializable

Реализация интерфейса IXmlSerializable дает значительно больше возможностей по управлению сериализацией и десериализацией, чем использование атрибутов. Объявление интерфейса выглядит следующим образом:

public interface IXmlSerializable

{

  XmlSchema GetSchema();

  void ReadXml (XmlReader reader);

  void WriteXml (XmlWriter writer);

}

Метод ReadXml должен читать внешний начальный элемент, затем содержимое и внешний конечный элемент. Метод WriteXml должен записывать только содержимое.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

using System;

using System.Xml;

using System.Xml.Schema;

using System.Xml.Serialization;

public class Address : IXmlSerializable

{

  public string Street, PostCode;

  public XmlSchema GetSchema() { return null; }

  public void ReadXml(XmlReader reader)

  {

      reader.ReadStartElement();

      Street = reader.ReadElementContentAsString («Street», «»);

      PostCode = reader.ReadElementContentAsString («PostCode», «»);

      reader.ReadEndElement();

  }

  public void WriteXml (XmlWriter writer)

  {

      writer.WriteElementString («Street», Street);

      writer.WriteElementString («PostCode», PostCode);

  }

}

XmlSerializer при сериализации и десериализации будет автоматически вызывать методы WriteXml и ReadXml.

Всем доброго времени суток, написал 2 метода 1-й сериализации, 2-й — десериализации, и при десериализации получаю следующие исключение:

Не удалось привести тип объекта «System.Int32» к типу
«binary.Classes.Serializ».

[Serializable]
    class Serializ
    {
        public Int32 Line { get; set; }

        BinaryFormatter formatter = new BinaryFormatter();
        public void SerializSave (Int32 _line)
        {
            Line = _line;

            using (FileStream fs = new FileStream(@"E:DELETEnr.dat", FileMode.Append))
            {
                formatter.Serialize(fs, Line);

                Console.WriteLine("Объект сериализован");
            }
        }

        public void SerializRead()
        {
            using (FileStream fs = new FileStream(@"E:DELETEnr.dat", FileMode.Open))
            {
                Serializ _serial = (Serializ)formatter.Deserialize(fs);

                Console.WriteLine("Объект десериализован");

                Console.WriteLine("Прочитано: {0}", _serial.Line);
            }
        }
    }

Как исправить ошибку данного характера ? что я не так сделал ? спасибо

Спасибо за пример, но теперь я получаю такое исключение:

Additional information: Тип
«System.Runtime.Serialization.Formatters.Binary.BinaryFormatter» в
сборке «mscorlib, Version=4.0.0.0, Culture=neutral,

Пробовал и так FileMode.Append и так FileMode.Create
Странно что на этот раз не так? и еще хотел спросить FileMode.Create — даст мне дозапись данных в файл ? мне это важно. Спасибо

Ошибка сериализации пустых табличных частей

Я
   vitek1

09.06.15 — 16:28

Ошибка заключается при десериализации объекта с пустой табличной частью, если в базе эта таб. часть заполнена. В этом случае заполненная таб. часть не очищается.

Поясню на примере.

Есть демо база, в ней док-т Поступление товаров и услуг с 2-ми таб. частями: Товары и Услуги (скрин Конфигуратор).

Для сериализации и десериализации использую коды (http://pixs.ru/showimage/RRRSRRSSRS_2575358_17611037.png):

[code]

&НаСервере

Процедура ВыгрузитьНаСервере()

      
      ЗаписьXML = Новый ЗаписьXML;

      ЗаписьXML.ОткрытьФайл(ИмяФайла, «windows-1251»);

      ЗаписьXML.ЗаписатьОбъявлениеXML();

      ЗаписьXML.ЗаписатьНачалоЭлемента(«Данные»);

      СериализаторXDTO.ЗаписатьXML(ЗаписьXML, Документ.ПолучитьОбъект());

      ЗаписьXML.ЗаписатьКонецЭлемента();

      ЗаписьXML.Закрыть();

      
КонецПроцедуры

&НаКлиенте

Процедура Выгрузить(Команда)

      ВыгрузитьНаСервере();

КонецПроцедуры

&НаСервере

Процедура ЗагрузитьНаСервере()

      
      ЧтениеXML = Новый ЧтениеXML;

      ЧтениеXML.ОткрытьФайл(ИмяФайла);

      ЧтениеXML.ПерейтиКСодержимому();

      ЧтениеXML.Прочитать();

      ДокументОбъект = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);

      ДокументОбъект.ОбменДанными.Загрузка = Истина;

      ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

      ЧтениеXML.Прочитать();

      ЧтениеXML.Закрыть();

      
      Документ = ДокументОбъект.Ссылка;

      
КонецПроцедуры

&НаКлиенте

Процедура Загрузить(Команда)

      ЗагрузитьНаСервере();

КонецПроцедуры

[code]

Сериализуем док-т с пустой таб. частью Товары, получаем xml:

[code]

<?xml version=»1.0″ encoding=»windows-1251″?>

<Данные>

      <DocumentObject.ПоступлениеТоваровИУслуг xmlns=»http://v8.1c.ru/8.1/data/enterprise/current-config»; xmlns:xs=» http://www.w3.org/2001/XMLSchema »»» xmlns:xsi=» http://www.w3.org/2001/XMLSchem… »»»>

            <Ref>3580c752-0e94-11e5-a111-c8d3a3841935</Ref>

            <DeletionMark>false</DeletionMark>

            <Date>2015-06-09T14:23:20</Date>

            <Number>000000001</Number>

            <Posted>true</Posted>

            <Услуги>

                  <Номенклатура>3580c751-0e94-11e5-a111-c8d3a3841935</Номенклатура>

            </Услуги>

      </DocumentObject.ПоступлениеТоваровИУслуг>

</Данные>

[code]

далее добавляем одну или несколько строк в таб. вчасть Товары в этот же док-т. Загружаем полученный ранее xml файл. После оператора ДокументОбъект = СериализаторXDTO.ПрочитатьXML(ЧтениеXML); в отладчике видим, что ТЧ Товары действительно пустая (http://pixs.ru/showimage/RSRRRRR.pn_1376624_17611041.png). После строки Записать тоже у объекта Товары пустая (а Ссылка.Товары заполнена). Но после отработки кода таб. часть Товары остается заполненной (при просмотре в режиме предприятия или кодом выполнить ПолучитьОбъект()).

Никаких отмен транзакций нет, загружаемые поля из файла модифицируются (например, если в файле руками поменять номер, то после загрузки он действительно меняется).

Для эксперимента пробовал в xml файл руками добавить <Товары/> перед тэгом услуги, тогда ругается рпи чтении.

Выкладываю также саму базу.

Как при этом можно использовать механизм сериализации серез СериализаторXDTO, если он не очищает пустые таб. части при загрузке?

Можно обойти эту ошибку без «ручной» сериализации объектов.

PS. ЗаписатьXML (глобавльный контекст, а не метод СериализаторXDTO) работает нормально, он добавляет <Товары/>, но он не сериализует, например, таблицу значений и прочие типы.

   MrStomak

1 — 09.06.15 — 16:32

XML и XDTO-сериализация объектов с табличными частями

Код ошибки: 30019662

Код(ы) обращения: SW915344, CSR-2226

Статус: Исправлена в будущей версии Зарегистрирована: 05.03.2015

Описание:

При чтении из XML (средствами XML и XDTO-сериализации) объектов с табличными частями, в случае, если в данных XML не содержится строк той или иной табличной части (табличная часть пустая), для существующих в БД объектов, при записи — очистки данной табличной части не произойдет.

Способ обхода:

Перед записью объекта для пустых табличных частей выполнять явную очистку при помощи метода Очистить.

  

vitek1

2 — 09.06.15 — 19:04

спасибо за ответ, очень помогли.

странно, что с 05.03.2015 1С не устранило ошибку, релизы-то выходили. Активно развивают и рекламируют универсальные кроссплатформенные средства обмена xml и json, но с этой ошибкой удобным и простым типовым механизмом пользоваться нельзя.

Для таких же несчастных, как я выкладываю универсальную процедуру, которую надо добавить после

ДокументОбъект = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);

для обхода ошибки. Вызываем

ОчиститьПустыеТабличныеЧасти(ДокументОбъект );

Процедура ОчиститьПустыеТабличныеЧасти(Значение) Экспорт

    
    Если ЭтоОбъект(Значение) Тогда

        МетаданныеОбъекта = Значение.Метаданные();

        Для каждого ТЧ Из МетаданныеОбъекта.ТабличныеЧасти Цикл

            Если Значение[ТЧ.Имя].Количество() = 0 Тогда

                Значение[ТЧ.Имя].Очистить();

            КонецЕсли;

        КонецЦикла;

    КонецЕсли;

    
КонецПроцедуры

// Проверка того, что переданный тип является объектым типом данных.

//

// Возвращаемое значение:

//  Булево.

//

Функция ЭтоОбъект(Значение) Экспорт

    
    Тип = XMLТипЗнч(Значение).ИмяТипа;

    
    Возврат Лев(Тип, 14) = «CatalogObject.»    

        ИЛИ Лев(Тип, 15) = «DocumentObject.»

        ИЛИ Лев(Тип, 19) = «ExchangePlanObject.»;        

    
КонецФункции

у меня используется для мобильного приложения. Для настольного функцию ЭтоОбъект() расширить на остальные типы данных

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Ошибка сериализации файла настроек key collector
  • Ошибка сети при скачивании картинки яндекс