I am trying to use DelayedJob and the job is failing, giving the following error in the database:
{Delayed::DeserializationError
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/serialization/active_record.rb:7:in `yaml_new'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `transfer'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `node_import'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `load'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `load'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/backend/base.rb:79:in `payload_object'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/backend/base.rb:87:in `invoke_job_without_newrelic_transaction_trace'
(eval):3:in `invoke_job'
/Library/Ruby/Gems/1.8/gems/newrelic_rpm-2.13.4/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:252:in `perform_action_with_newrelic_trace'
/Library/Ruby/Gems/1.8/gems/newrelic_rpm-2.13.4/lib/new_relic/agent/method_tracer.rb:141:in `trace_execution_scoped'
/Library/Ruby/Gems/1.8/gems/newrelic_rpm-2.13.4/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:247:in `perform_action_with_newrelic_trace'
(eval):2:in `invoke_job'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:120:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/timeout.rb:62:in `timeout'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:120:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/benchmark.rb:308:in `realtime'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:119:in `run'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:177:in `reserve_and_run_one_job'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:104:in `work_off'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:103:in `times'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:103:in `work_off'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:78:in `start'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/benchmark.rb:308:in `realtime'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:77:in `start'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:74:in `loop'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:74:in `start'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/tasks.rb:9
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `call'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `execute'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `execute'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:597:in `invoke_with_call_chain'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/monitor.rb:242:in `synchronize'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:583:in `invoke'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2051:in `invoke_task'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2023:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2001:in `run'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:1998:in `run'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/bin/rake:31
/usr/bin/rake:19:in `load'
/usr/bin/rake:19
Not sure where to start in diagnosing this. This has never happened before and I have used delayed job before to serialize model objects without any issues. Why this time?
Thanks in advance!
John Bachir
22.4k28 gold badges152 silver badges222 bronze badges
asked Feb 16, 2011 at 23:01
8
It’s not really a deserialization error, it’s an ActiveRecord record-not-found error on a simple Model.find(id) query.
If you want to know the details, log them in the delayed_job-2.1.3/lib/delayed/serialization/active_record.rb file, in the rescue statement, just before delayed-job stupidly raises the DeserializationError and throws the useful information away.
answered Feb 28, 2011 at 14:32
Michiel de MareMichiel de Mare
41.7k29 gold badges102 silver badges134 bronze badges
1
Michiel is right. Look at your handler field for objects like «!ruby/ActiveRecord:YourClassName»
Then check if objects can be retrieved via the primary key
From the console you can also test this out by doing something like:
# first job in your delayed queue
YAML.load(Delayed::Backend::ActiveRecord::Job.first.handler)
answered Mar 27, 2011 at 23:11
TonyTony
18.5k31 gold badges127 silver badges191 bronze badges
I believe this occurs when you run a job against an unsaved or deleted AR object since the deserialization for AR loads the record by id. An exception should probably be thrown if you attempt to delay a method on an unsaved AR object.
answered Jun 24, 2011 at 9:18
Harshal_m_joshiHarshal_m_joshi
1,4891 gold badge16 silver badges27 bronze badges
1
There’s also a documented bug with DJ when the params passed into the handler field in the DB are longer than a standard TEXT column:
https://github.com/collectiveidea/delayed_job/issues/491
If this happens to be your problem, changing the column to a MEDIUMINT should fix the issue.
I did this in a migration like so:
change_column :delayed_jobs, :handler, :text, :limit => 16777215
ActiveRecord::Base.connection.execute("DELETE FROM delayed_jobs WHERE LENGTH(handler) >= 65535")
You can check to see if it’s an issue with a simple DB query:
SELECT * FROM delayed_jobs WHERE LENGTH(handler) >= 65535
answered Dec 20, 2013 at 17:46
ideaoforderideaoforder
1,0232 gold badges9 silver badges23 bronze badges
If anyone wants to make delayed_job just finish the job as a no-op you can monkey patch with this code in the initializer:
https://gist.github.com/spilliton/8494752
answered Jan 18, 2014 at 19:10
spillitonspilliton
3,7715 gold badges35 silver badges35 bronze badges
Today, I also suffered through this error and after doing hectic analysis found:
- delayed_job converts methods & parameters to YAML format and stores it into database
- It can be found using
select * from delayed_jobs; - deserialization error occurs when delayed_job is not able to deserialize it.
Probable causes can be:
- args[«xyz»] used before calling delayed_job & inside worker using it as args[:xyz]
- Sometimes extra arguments passed along with object to delayed_job that time delayed_job fails to build object as it is indifferent access.
I Hope this will help!
John Bachir
22.4k28 gold badges152 silver badges222 bronze badges
answered Mar 2, 2011 at 19:02
Sandip RansingSandip Ransing
7,4634 gold badges36 silver badges48 bronze badges
Sometimes when we upgrade libs delayed jobs still keep old references.
Try to find the id of delayed_job in logs and play to parse its handler to ruby to find the wrong reference
j = DelayedJob.find(XXX)
data = YAML.load_dj(j.handler)
data.to_ruby
I made a pull request to help with this problem.
Meanwhile you can use this lines
# config/initializers/delayed_job.rb
# Monkey patch to use old class references
module Psych
class << self; attr_accessor :old_class_references end
@old_class_references = {}
class ClassLoader
private
def find klassname
klassname = ::Psych.old_class_references[klassname] || klassname
@cache[klassname] ||= resolve(klassname)
end
end
module Visitors
class ToRuby < Psych::Visitors::Visitor
def revive klass, node
if klass.is_a? String
klassname = ::Psych.old_class_references[klass] || klass
klass = Kernel.const_get(klassname) rescue klassname
end
s = register(node, klass.allocate)
init_with(s, revive_hash({}, node), node)
end
end
end
end
# Add all old dependencies (hash keys) pointing to new references (hash values)
Psych.old_class_references = {
'ActiveRecord::AttributeSet' => 'ActiveModel::AttributeSet'
# ...
}
answered Jun 7, 2019 at 17:25
I am trying to use DelayedJob and the job is failing, giving the following error in the database:
{Delayed::DeserializationError
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/serialization/active_record.rb:7:in `yaml_new'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `transfer'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `node_import'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `load'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml.rb:133:in `load'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/backend/base.rb:79:in `payload_object'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/backend/base.rb:87:in `invoke_job_without_newrelic_transaction_trace'
(eval):3:in `invoke_job'
/Library/Ruby/Gems/1.8/gems/newrelic_rpm-2.13.4/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:252:in `perform_action_with_newrelic_trace'
/Library/Ruby/Gems/1.8/gems/newrelic_rpm-2.13.4/lib/new_relic/agent/method_tracer.rb:141:in `trace_execution_scoped'
/Library/Ruby/Gems/1.8/gems/newrelic_rpm-2.13.4/lib/new_relic/agent/instrumentation/controller_instrumentation.rb:247:in `perform_action_with_newrelic_trace'
(eval):2:in `invoke_job'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:120:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/timeout.rb:62:in `timeout'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:120:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/benchmark.rb:308:in `realtime'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:119:in `run'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:177:in `reserve_and_run_one_job'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:104:in `work_off'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:103:in `times'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:103:in `work_off'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:78:in `start'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/benchmark.rb:308:in `realtime'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:77:in `start'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:74:in `loop'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/worker.rb:74:in `start'
/Library/Ruby/Gems/1.8/gems/delayed_job-2.1.3/lib/delayed/tasks.rb:9
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `call'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:636:in `execute'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:631:in `execute'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:597:in `invoke_with_call_chain'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/monitor.rb:242:in `synchronize'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:590:in `invoke_with_call_chain'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:583:in `invoke'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2051:in `invoke_task'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `each'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2029:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2023:in `top_level'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2001:in `run'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:2068:in `standard_exception_handling'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/lib/rake.rb:1998:in `run'
/Library/Ruby/Gems/1.8/gems/rake-0.8.7/bin/rake:31
/usr/bin/rake:19:in `load'
/usr/bin/rake:19
Not sure where to start in diagnosing this. This has never happened before and I have used delayed job before to serialize model objects without any issues. Why this time?
Thanks in advance!
John Bachir
22.4k28 gold badges152 silver badges222 bronze badges
asked Feb 16, 2011 at 23:01
8
It’s not really a deserialization error, it’s an ActiveRecord record-not-found error on a simple Model.find(id) query.
If you want to know the details, log them in the delayed_job-2.1.3/lib/delayed/serialization/active_record.rb file, in the rescue statement, just before delayed-job stupidly raises the DeserializationError and throws the useful information away.
answered Feb 28, 2011 at 14:32
Michiel de MareMichiel de Mare
41.7k29 gold badges102 silver badges134 bronze badges
1
Michiel is right. Look at your handler field for objects like «!ruby/ActiveRecord:YourClassName»
Then check if objects can be retrieved via the primary key
From the console you can also test this out by doing something like:
# first job in your delayed queue
YAML.load(Delayed::Backend::ActiveRecord::Job.first.handler)
answered Mar 27, 2011 at 23:11
TonyTony
18.5k31 gold badges127 silver badges191 bronze badges
I believe this occurs when you run a job against an unsaved or deleted AR object since the deserialization for AR loads the record by id. An exception should probably be thrown if you attempt to delay a method on an unsaved AR object.
answered Jun 24, 2011 at 9:18
Harshal_m_joshiHarshal_m_joshi
1,4891 gold badge16 silver badges27 bronze badges
1
There’s also a documented bug with DJ when the params passed into the handler field in the DB are longer than a standard TEXT column:
https://github.com/collectiveidea/delayed_job/issues/491
If this happens to be your problem, changing the column to a MEDIUMINT should fix the issue.
I did this in a migration like so:
change_column :delayed_jobs, :handler, :text, :limit => 16777215
ActiveRecord::Base.connection.execute("DELETE FROM delayed_jobs WHERE LENGTH(handler) >= 65535")
You can check to see if it’s an issue with a simple DB query:
SELECT * FROM delayed_jobs WHERE LENGTH(handler) >= 65535
answered Dec 20, 2013 at 17:46
ideaoforderideaoforder
1,0232 gold badges9 silver badges23 bronze badges
If anyone wants to make delayed_job just finish the job as a no-op you can monkey patch with this code in the initializer:
https://gist.github.com/spilliton/8494752
answered Jan 18, 2014 at 19:10
spillitonspilliton
3,7715 gold badges35 silver badges35 bronze badges
Today, I also suffered through this error and after doing hectic analysis found:
- delayed_job converts methods & parameters to YAML format and stores it into database
- It can be found using
select * from delayed_jobs; - deserialization error occurs when delayed_job is not able to deserialize it.
Probable causes can be:
- args[«xyz»] used before calling delayed_job & inside worker using it as args[:xyz]
- Sometimes extra arguments passed along with object to delayed_job that time delayed_job fails to build object as it is indifferent access.
I Hope this will help!
John Bachir
22.4k28 gold badges152 silver badges222 bronze badges
answered Mar 2, 2011 at 19:02
Sandip RansingSandip Ransing
7,4634 gold badges36 silver badges48 bronze badges
Sometimes when we upgrade libs delayed jobs still keep old references.
Try to find the id of delayed_job in logs and play to parse its handler to ruby to find the wrong reference
j = DelayedJob.find(XXX)
data = YAML.load_dj(j.handler)
data.to_ruby
I made a pull request to help with this problem.
Meanwhile you can use this lines
# config/initializers/delayed_job.rb
# Monkey patch to use old class references
module Psych
class << self; attr_accessor :old_class_references end
@old_class_references = {}
class ClassLoader
private
def find klassname
klassname = ::Psych.old_class_references[klassname] || klassname
@cache[klassname] ||= resolve(klassname)
end
end
module Visitors
class ToRuby < Psych::Visitors::Visitor
def revive klass, node
if klass.is_a? String
klassname = ::Psych.old_class_references[klass] || klass
klass = Kernel.const_get(klassname) rescue klassname
end
s = register(node, klass.allocate)
init_with(s, revive_hash({}, node), node)
end
end
end
end
# Add all old dependencies (hash keys) pointing to new references (hash values)
Psych.old_class_references = {
'ActiveRecord::AttributeSet' => 'ActiveModel::AttributeSet'
# ...
}
answered Jun 7, 2019 at 17:25
Бывают ситуации, когда пользователь программы 1С сталкивается с появлением ошибки – «Ошибка десериализации контактной информации не указан ожидаемый тип». Возникает она в основном при переходе на новую редакцию программы или обновлении конфигурации и связана с некорректной конвертацией справочника «Виды контактной информации», а появляется при попытке занести данные.
Данные «Контактная информация» задействованы в справочниках «Организация», «Подразделения организации», «Контрагенты», «Контактные лица». Их можно просмотреть, открыв раздел «Справочники», используя пункт меню «Все функций». Если этот раздел не доступен, включить его можно в разделе «Сервис» — «Параметры».


Пример заполнения «Контактной информации» в справочнике «Контрагенты».

Если мы откроем и посмотрим настройку элемента справочника «Контактная информация», например, «Юридический адрес», то мы увидим, что в поле «Тип» установлено значение «Адрес». Это верное значение, для данного вида информации.

Но, возможно, здесь некорректное отображение по каким-либо причинам, например, в процессе переноса данных из старой версии в новую могли быть перенесены лишние элементы. Их можно удалить. Также надо проверить остальные виды контактной информации, чтобы там не было пустых и неправильно заполненных полей. В случае обнаружения недочета, его также необходимо устранить.
Открыв любой вид контактной информации, мы видим, что в поле нет кнопки выбора, чтобы сменить (или установить в случае отсутствия) тип, но можно выйти из ситуации, используя копирование элемента и установку пометки на удаления неправильного элемента. В этом случае кнопка выбора типа информации активна.

После корректировки типа представления информации ошибка десериализации появляться не будет.
Еще одним способом устранения проблемы (более простым) является использование обработки по установке типа контактной информации «УстановкаТиповИВидовКИ». Ее можно найти в интернете, она написана сторонними разработчиками и для обычных форм, и для управляемых. С ее помощью можно изменить тип любого элемента справочника «Контактная информация».

Выбираем элемент, который необходимо исправить, задаем правильный тип и нажимаем кнопку «Установить тип».

С изменением типа представления контактной информации ошибка появляться не будет. При этом необходимо обратить внимание, на какой элемент программа «ругается» – это может быть не только адрес, но и, например, телефон, адрес электронной почты. Тогда действие производится с указанным элементом.
Как уже успел сказать @Anatol в комментариях, ошибка вызвана тем, что Вы не переместили указатель на начало потока и чтение при десериализации начинается с его конца
Чтобы исправить это, достаточно установить свойство Stream.Position равным 0 (так как начало записи сериализованного объекта равняется началу самого потока)
То есть:
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter fmt = new BinaryFormatter();
stream.Write(data, 0, data.Length);
stream.Position = 0; // Ставим указатель на начало
endDataPacket = fmt.Deserialize(stream).To<Packet>(); // Успешная десериализация
}
Не совсем относится к вопросу, но хочу заметить одну вещь:
Как я понимаю, Вы создаете некий мост между двумя сервисами, способными принимать/отправлять некоторого вида пакеты
Использовать System.Runtime.Serialization для этого не есть хорошо
Посмотрите: Вы храните 512 байтов информации, а при сериализации получаете 1024 (data.Length), что аж в 2 раза больше (прямой зависимости вроде 1 к 2 нет, но сериализация, используемая Вами, всегда выдает достаточно громоздкие (по отношению к реальным) массивы данных)
В случае, если Вы можете полностью отказаться от ссылочных типов данных внутри Вашей структуры, могу предложить Вам следующий вариант:
// Пример пакета
public unsafe struct Packet
{
// System.Int32 и без того является типом значения
public int userID;
// Для примера сделал фиксированное поле
public fixed byte userMessage[256];
public Packet(int userID, byte[] userMessage)
{
this.userID = userID;
fixed (byte* id = this.userMessage)
for (int i = 0; i < 255; ++i)
id[i] = userMessage[i];
}
}
Расширение для демонстрации:
public static class StructExtensions
{
public static byte[] Zip<T>(this T Obj) where T : struct
{
// Получим размер, занимаемый объектом
int size = Marshal.SizeOf(Obj);
// Сюда будем читать данные
byte[] bytes = new byte[size];
// Выделим память и получим указатель на выделенный блок
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
// Пишем данные структуры в неуправляемый блок памяти
Marshal.StructureToPtr(Obj, ptr, false);
// Копируем данные
Marshal.Copy(ptr, bytes, 0, size);
}
catch (Exception exception)
{
// Даже в случае ошибки память следует освободить
Marshal.FreeHGlobal(ptr);
throw exception;
}
Marshal.FreeHGlobal(ptr);
return bytes;
}
// Не надо создавать расширения для стандартных типов
// Здесь я это делаю чисто ради наглядности примера
public static T Unzip<T>(this byte[] Bytes) where T : struct
{
// Получаем дексриптор (указываем, что он закреплен, чтобы сборщик мусора не баловался)
GCHandle handle = GCHandle.Alloc(Bytes, GCHandleType.Pinned);
try
{
// Читаем структуру из блока памяти
T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
// Возвращаем ее
return theStructure;
}
catch (Exception exception)
{
// Даже в случае ошибки память следует освободить
handle.Free();
throw exception;
}
}
}
И сам тест:
// Подготовим тестовый массив данных
byte[] userId = Enumerable.Range(0, 255).Select(x => (byte)x).ToArray();
// Инициализируем объект
Packet packet = new Packet(10, userId);
// Сериализуем его и тут же десериализуем обратно
Packet endDataPacket = packet.Zip().Unzip<Packet>(); // userID == 10, userMessage = [0..255]
Заметьте, что метод .Zip() вернет нам массив байт длинной 260 (4 на int и 256 на массив), то есть ровно столько, сколько мы и храним
Суть данного метода в том, что мы просто копируем участок памяти, занимаемый структурой, в массив байт. Потом этот массив мы можем точно так же записать в участок памяти, отведенный под аналогичную (хотя бы по размерам) структуру
Именно поэтому важно отказаться (в рамках данного метода) от ссылочных типов, ибо на их «месте» находятся указатели на иные блоки памяти, где и хранятся их данные. Тем самым в рамках одной и той же машины копирование структуры, которая включает в себя ссылочные типы (вроде массивов), пройдет успешно (если указатели на объекты не сменились), а вот при передаче такого блока на удаленную машину Вы получите результаты весьма неожиданные (точнее, неопределенные)
Я не знаю конкретики Вашей ситуации, так что не принимайте мой совет за истину
Ибо это просто совет, который к тому же и не для всех ситуаций применим)
|
MakcPletnev 41 / 37 / 9 Регистрация: 01.02.2014 Сообщений: 825 |
||||||||
|
1 |
||||||||
Ошибка десериализации объекта05.04.2016, 00:24. Показов 5687. Ответов 9 Метки нет (Все метки)
Ошибка десериализации объекта типа RespGroup1. Символы с шестнадцатеричными значениями 0xFFFE и 0xFFFF являются недопустимыми В общем, не может дисериализировать такой вот JSON (Именно этот пример, плейлист большинства дисериализирует без проблем, 1 раз такая проблема):
Код:
Помогите пожалуйста, добрые люди) Добавлено через 13 секунд Добавлено через 9 минут
__________________
0 |
|
Jason 525 / 487 / 98 Регистрация: 25.12.2011 Сообщений: 1,176 |
||||
|
05.04.2016, 00:37 |
2 |
|||
|
Просили — помогаю
P.S. Только я удалил — {«response»:[ в начале и ]} в конце (для удобства).
0 |
|
41 / 37 / 9 Регистрация: 01.02.2014 Сообщений: 825 |
|
|
05.04.2016, 00:44 [ТС] |
3 |
|
Jason, попробуй это: https://yadi.sk/d/YG60-YYiqjhe9 Добавлено через 4 минуты
0 |
|
525 / 487 / 98 Регистрация: 25.12.2011 Сообщений: 1,176 |
|
|
05.04.2016, 00:45 |
4 |
|
Это уже издевательство, был пример выше, я его разобрал, а тут на 6 метров не пойми чего, я это нормально даже прочитать не могу.
0 |
|
41 / 37 / 9 Регистрация: 01.02.2014 Сообщений: 825 |
|
|
05.04.2016, 00:46 [ТС] |
5 |
|
Jason, это тот же самый json , с описанием из шапки. Фишка в том, что выдает ошибку
0 |
|
Jason 525 / 487 / 98 Регистрация: 25.12.2011 Сообщений: 1,176 |
||||||||||||
|
05.04.2016, 01:59 |
6 |
|||||||||||
|
Вот так нужно было урезать структуру:
Так, а теперь код:
P.S.
Символы с шестнадцатеричными значениями 0xFFFE и 0xFFFF являются недопустимыми», надо решить вопрос именно с ней… А на счет этого, не нужно файлы сохранять в формате Юникод и т.п. Сохраните файл в ANSI и проверяем… Либо пробовать что-то вроде этого:
0 |
|
41 / 37 / 9 Регистрация: 01.02.2014 Сообщений: 825 |
|
|
06.04.2016, 21:04 [ТС] |
7 |
|
Jason, из-за этого не поддерживается кириллица, я пробовал уже Добавлено через 1 минуту
0 |
|
975 / 870 / 350 Регистрация: 26.04.2012 Сообщений: 2,647 |
|
|
07.04.2016, 02:36 |
8 |
|
Я большой JSON обычно не десериализую, а использую при при помощи библиотеки Newtonsoft.Json и тамошнего класса JToken.
0 |
|
41 / 37 / 9 Регистрация: 01.02.2014 Сообщений: 825 |
|
|
26.07.2016, 11:06 [ТС] |
9 |
|
EvilFromHell, может расскажете как?
0 |
|
Master of Orion 6094 / 4950 / 905 Регистрация: 10.07.2011 Сообщений: 14,522 Записей в блоге: 5 |
|
|
27.07.2016, 00:06 |
10 |
|
EvilFromHell, забавно, что я в DataContractSerializer при сериализации в XML ловил точно такую же ошибку. Решалось просто вырезанием всего, что не проходит проверку XmlConvert.IsXmlChar. Вполне возможно, что JSON реально не ест эти символы (это именно некорректные символы, а не кириллица, например если суррогатная пара применена с символом, с которой её использование недопустимо). Не уверен, что JSON.Net поможет, хотя я в любом случае его использую во всех случаях работы с JSON. Слишком удобно чтобы отказываться.
1 |
Содержание
- DeserializationError
- Description
- Values
- DeserializationError::Ok
- DeserializationError::EmptyInput
- DeserializationError::IncompleteInput
- DeserializationError::InvalidInput
- DeserializationError::NoMemory
- DeserializationError::NotSupported ⚠️ removed
- DeserializationError::TooDeep
- Methods
- How to know where deserialization stopped?
- Serialization Error Handling
- Как не наступить на грабли, работая с сериализацией
- Для кого статья?
- Реализуя ISerializable, не забудьте про конструктор сериализации
- Обращайте внимание на модификатор доступа конструктора сериализации
- Конструктор сериализации в незапечатанном классе имеет модификатор доступа ‘private’
- Не объявляйте конструктор сериализации с модификаторами ‘public’ или ‘internal’
- Реализуйте виртуальный метод GetObjectData в незапечатанных классах
- Все сериализуемые члены должны иметь сериализуемый тип
- Не забывайте про атрибут [Serializable] при реализации интерфейса ISerializable
- Убедитесь, что в методе GetObjectData сериализуются все необходимые члены типа
- Обобщение
- Заключение
- Дополнительная информация
DeserializationError
Description
Values
A DeserializationError is an enumerated type that can contain one of the following values:
DeserializationError::Ok
The deserialization succeeded. Cool!
DeserializationError::EmptyInput
The input was empty or contained only spaces or comments. Possible reasons:
- a timeout occurred when reading from a stream (see How to change the timeout)
- the server returned a redirection code (like 301 Moved Permanently or 302 Found)
- the certificate validation failed (try WiFiClientSecure::setInsecure() )
- the stream is not connected
- the file was not found
- the file is empty
- the file was opened in the wrong mode
EmptyInput was added in ArduinoJson 6.17.0
DeserializationError::IncompleteInput
The end of the input is missing. Possible reasons:
- a buffer was too small to contains the input
- a timeout occurred when reading from a stream (see How to change the timeout)
Before ArduinoJson 6.17.0, IncompleteInput could also mean “empty input.”
DeserializationError::InvalidInput
The input is not recognized. Possible reasons:
- the input is simply invalid
- the input contains a comment, but support is disabled 🆕
- the input is valid but is preceded by something else (see below)
- you called deserializeJson() twice in zero-copy mode
If this error occurs on an HTTP response, ensure your program:
- skips the HTTP headers
- uses HTTP version 1.0 or handles chunked transfer encoding
This error also occurs if the input document starts with a byte order mark (BOM). This problem is hard to diagnose because the BOM is an invisible character, so you can’t see it in the Serial Monitor and in most text editors. The easiest way to check if the BOM is present is to read the first byte of the input (for example, you can do Serial.print((char)client.read()) ). The first character should be a < or a [ ; if you see something else, then it’s surely the BOM. If that’s your case, you must skip the first two bytes before calling deserializeJson() , like so:
Of course, the best solution is still to remove the BOM from the server side.
DeserializationError::NoMemory
The JsonDocument is too small; you need to increase its capacity.
When using a DynamicJsonDocument , it can also mean that the allocation of the memory pool failed.
You can check whether the allocation succeeded by looking at JsonDocument::capacity() .
DeserializationError::NotSupported ⚠️ removed
The document included features not supported by the parser. Possible reasons:
- the JSON input contains a Unicode escape sequence (like u0032 ), but support is disabled
- the MessagePack input contains a binary value
- the MessagePack input contains an object key that is not a string
NotSupported was removed in ArduinoJson 6.18.0. Instead, deserializeJson() ignores the Unicode escape sequences when ARDUINOJSON_DECODE_UNICODE is 0 (the default is 1 since 6.16), and deserializeMsgPack() replaces unsupported values with nulls.
DeserializationError::TooDeep
The nesting limit was reached; you need to increase its value. See deserializeJson() , or ARDUINOJSON_DEFAULT_NESTING_LIMIT for details.
Methods
How to know where deserialization stopped?
When you pass a Stream to deserializeJson() , it consumes the input but doesn’t print anything to the serial, which makes troubleshooting difficult.
If you want to see what deserializeJson() consumed, use ReadLoggingStream from the StreamUtils library (see example below). Because ArduinoJson stops reading as soon as it sees an error, you can see what caused the error by checking the last consumed character.
Источник
Serialization Error Handling
Json.NET supports error handling during serialization and deserialization. Error handling lets you catch an error and choose whether to handle it and continue with serialization or let the error bubble up and be thrown in your application.
Error handling is defined through two methods: the Error event on JsonSerializer and the OnErrorAttribute.
The Error event is an event handler found on JsonSerializer. The error event is raised whenever an exception is thrown while serializing or deserializing JSON. Like all settings found on JsonSerializer, it can also be set on JsonSerializerSettings and passed to the serialization methods on JsonConvert.
In this example we are deserializing a JSON array to a collection of DateTimes. On the JsonSerializerSettings a handler has been assigned to the Error event which will log the error message and mark the error as handled.
The result of deserializing the JSON is three successfully deserialized dates and three error messages: one for the badly formatted string («I am not a date and will error!»), one for the nested JSON array, and one for the null value since the list doesn’t allow nullable DateTimes. The event handler has logged these messages and Json.NET has continued on deserializing the JSON because the errors were marked as handled.
One thing to note with error handling in Json.NET is that an unhandled error will bubble up and raise the event on each of its parents. For example an unhandled error when serializing a collection of objects will be raised twice, once against the object and then again on the collection. This will let you handle an error either where it occurred or on one of its parents.
If you aren’t immediately handling an error and only want to perform an action against it once, then you can check to see whether the ErrorEventArgs’s CurrentObject is equal to the OriginalObject. OriginalObject is the object that threw the error and CurrentObject is the object that the event is being raised against. They will only equal the first time the event is raised against the OriginalObject.
Источник
Как не наступить на грабли, работая с сериализацией
Несмотря на то, что использовать механизм сериализации при программировании на C# достаточно просто и удобно, есть моменты, которые стоит учитывать. О том, на какие грабли можно наступить, работая с сериализацией, о примерах кода, в котором эти грабли припрятаны, а также о том, как PVS-Studio поможет вам избежать шишек на лбу, и будет эта статья.
Для кого статья?
Данная статья будет особенно полезна разработчикам, только начинающим знакомиться с механизмом сериализации. Более опытные программисты также могут почерпнуть для себя что-то интересное, или просто убедиться, что даже профессионалы порой допускают ошибки.
Тем не менее, подразумевается, что читатель уже знаком с механизмом сериализации.
При чём тут PVS-Studio? В релизе 6.05 были добавлены 6 диагностических правил, обнаруживающих подозрительный код, связанный с использованием механизма сериализации. Эти диагностики в основном ищут проблемные места, связанные с атрибутом [Serializable] или реализацией интерфейса ISerializable.
Стоит понимать, что описанные в статье утверждения актуальны для некоторых сериализаторов, например — BinaryFormatter и SoapFormatter, а для других, например, собственноручно написанного сериализатора, поведение может отличаться. Например, отсутствие атрибута [Serializable] у класса может не помешать проводить его сериализацию и десериализацию собственным сериализатором.
Кстати, если вы работаете с сериализацией, советую загрузить пробную версию анализатора и проверить свой код на наличие подозрительных мест.
Реализуя ISerializable, не забудьте про конструктор сериализации
Реализация типом интерфейса ISerializable позволяет управлять сериализацией, выбирая, какие члены нужно сериализовать, какие — нет, какие значение нужно записывать при сериализации членов и т.п.
Интерфейс ISerializable содержит объявление одного метода — GetObjectData, который будет вызван при сериализации объекта. Но в паре с этим методом обязательно должен быть реализован конструктор, который будет вызываться при десериализации объекта. Так как интерфейс не может обязать вас реализовать в своем типе какой-то конструктор, эта задача ложится на плечи программиста, занимающегося реализацией сериализуемого типа. Конструктор сериализации имеет следующую сигнатуру:
Без наличия данного конструктора сериализация объекта пройдёт успешно (при условии корректной реализации метода GetObjectData), но восстановить (десериализовать) его не удастся — будет сгенерировано исключение типа SerializationException.
Посмотрим на пример подобного кода из проекта Glimpse:
Предупреждение PVS-Studio: V3094 Possible exception when deserializing. The SerializableTestObject(SerializationInfo, StreamingContext) constructor is missing. Glimpse.Test.AspNet SessionModelConverterShould.cs 111
Сериализация экземпляра данного класса пройдёт успешно, а вот при десериализации возникнет исключение, так как нет соответствующего конструктора. Скорее всего, это не ошибка (исходя из названия класса и файла), но в качестве иллюстрации описанной ситуации — то, что нужно.
Конструктор сериализации для данного класса мог бы выглядеть так:
Обращайте внимание на модификатор доступа конструктора сериализации
При разработке типа, реализующего интерфейс ISerializable, важно правильно определить модификатор доступа для конструктора сериализации. Здесь возможны несколько случаев:
- конструктор сериализации объявлен с модификатором private в незапечатанном классе;
- конструктор сериализации объявлен с модификатором доступа public или internal;
- конструктор сериализации объявлен с модификатором protected в запечатанном классе.
Наибольший интерес представляет первый из приведённых выше вариантов, так как он таит в себе наибольшую опасность. Кратко остановимся и на втором пункте, а третий рассматривать не будем — объявить член с модификатором protected в структуре не даст компилятор (ошибка компиляции), если такой член объявляется в запечатанном классе — компилятор выдаст предупреждение.
Конструктор сериализации в незапечатанном классе имеет модификатор доступа ‘private’
Это наиболее опасный случай из ситуаций неправильного применения модификаторов доступа к конструкторам сериализации. Если тип является незапечатанным, подразумевается, что у него могут быть наследники. Однако если конструктор сериализации имеет модификатор доступа private, он не сможет быть вызван из дочернего класса.
В таком случае у разработчика дочернего класса есть 2 выхода — отказаться от использования данного родительского класса или вручную десериализовать члены базового класса. Стоит отметить, что второй случай навряд ли можно считать решением проблемы:
- не факт, что в базовом классе была предусмотрена тривиальная десериализация членов;
- разработчик дочернего класса может забыть десереализовать какой-либо член базового класса;
- при всём желании, десериализовать приватные члены базового класса не удастся.
Поэтому, разрабатывая незапечатанный сериализуемый класс, обращайте внимание на то, какой модификатор доступа имеет конструктор сериализации.
При анализе проектов удалось найти несколько таких, в которых описанное выше правило не соблюдалось.
Предупреждение PVS-Studio: V3103 A private Ctor(SerializationInfo, StreamingContext) constructor in unsealed type will not be accessible when deserializing derived types. NHibernate ConnectionManager.cs 276
Предупреждение PVS-Studio: V3103 A private TestDiagnostic(SerializationInfo, StreamingContext) constructor in unsealed type will not be accessible when deserializing derived types. DiagnosticAnalyzerTests.cs 100
В обоих примерах, приведённых выше, у конструктора сериализации следовало установить модификатор доступа protected, чтобы дочерние классы могли вызвать его при десериализации.
Не объявляйте конструктор сериализации с модификаторами ‘public’ или ‘internal’
Это совет хорошего стиля программирования. Объявление конструктора сериализации с модификатором public или internal не приведёт к ошибке, но смысла в этом нет — данный конструктор не должен вызываться извне, а сериализатору без разницы, какой модификатор доступа имеет конструктор.
При проверке open source проектов встретились несколько таких, в которых это правило не соблюдалось.
Предупреждение PVS-Studio: V3103 The Ctor(SerializationInfo, StreamingContext) constructor should be used for deserialization. Making it internal is not recommended. Consider making it private. Microsoft.Build.Tasks SystemState.cs 218
Предупреждение PVS-Studio: V3103 The Ctor(SerializationInfo, StreamingContext) constructor should be used for deserialization. Making it internal is not recommended. Consider making it private. Microsoft.Build.Tasks SystemState.cs 139
В обоих случаях для конструктора сериализации следовало установить модификатор доступа private, так оба класса, приведённых выше, являются запечатанными.
Предупреждение PVS-Studio: V3103 The Ctor(SerializationInfo, StreamingContext) constructor should be used for deserialization. Making it internal is not recommended. Consider making it protected. NHibernate StatefulPersistenceContext.cs 1478
Предупреждение PVS-Studio: V3103 The Ctor(SerializationInfo, StreamingContext) constructor should be used for deserialization. Making it public is not recommended. Consider making it protected. NHibernate Configuration.cs 84
Ввиду того, что оба класса являются незапечатанными, для конструкторов сериализации следовало установить модификатор доступа protected.
Реализуйте виртуальный метод GetObjectData в незапечатанных классах
Правило простое — если вы разрабатываете незапечатанный класс, реализующий интерфейс ISerializable, объявите метод GetObjectData с модификатором virtual. Это позволит дочерним классам корректно производить сериализацию объекта при использовании полиморфизма.
Чтобы лучше понять суть проблемы, предлагаю рассмотреть несколько примеров.
Допустим, у нас есть следующие объявления родительского и дочернего классов.
Предположим, что имеется метод сериализации и десереализации объекта следующего вида:
В таком случае сериализация выполнится неправильно из-за того, что будет вызван метод GetObjectData не дочернего, а родительского класса. Следовательно, члены дочернего класса не будут сериализованы. Если при десериализации из объекта типа SerializationInfo будут извлекаться значения членов, добавляемых в методе GetObjectData дочернего класса, будет сгенерировано исключение, так как объект типа SerializationInfo не будет содержать запрашиваемых ключей.
Для исправления ошибки в родительском классе к методу GetObjectData необходимо добавить модификатор virtual, в производном — override.
Если в родительском классе присутствует только явная реализация интерфейса ISerializable, добавить к ней модификатор virtual вы не сможете. Однако оставив всё, как есть, вы рискуете усложнить жизнь разработчикам дочерних классов.
Рассмотрим пример реализации родительского и дочернего классов:
В таком случае из дочернего класса будет невозможно обратиться к методу GetObjectData родительского класса. И если в базовом методе сериализуются приватные члены, обратиться к ним из дочернего класса также не удастся, а значит и не удастся провести корректную сериализацию. Для исправления ошибки помимо явной реализации в базовый класс необходимо добавить неявную реализацию виртуального метода GetObjectData. Тогда исправленный код может выглядеть так:
Или же, если не подразумевается наследование данного класса, следует сделать его запечатанным, добавив к объявлению класса модификатор sealed.
Предупреждение PVS-Studio: V3104 ‘GetObjectData’ implementation in unsealed type ‘TestDiagnostic’ is not virtual, incorrect serialization of derived type is possible. CSharpCompilerSemanticTest DiagnosticAnalyzerTests.cs 112
Класс TestDiagnostic является незапечатанным (хоть и приватным, так что унаследоваться от него в рамках того же класса возможно), но при этом он имеет только явную реализацию интерфейса ISerializable, в которой, ко всему прочему, сериализуются приватные члены. Это означает одно — разработчик дочернего класса никаким образом не сможет сериализовать необходимые члены: метод GetObjectData будет ему недоступен, а обратиться к членам напрямую не позволит модификатор доступа.
Правильнее было бы вынести весь код сериализации, приведённый выше, в виртуальный метод GetObjectData, на который сослаться из явной реализации интерфейса:
Все сериализуемые члены должны иметь сериализуемый тип
Это условие является обязательным для корректной сериализации объекта вне зависимости от того, происходит ли автоматическая сериализация (когда тип декорирован атрибутом [Serializable] и при этом не реализует интерфейс ISerializable) или сериализация осуществляется вручную (реализован ISerializable).
В противном случае, если при сериализации встретится член, не декорированный атрибутом [Serializable], будет сгенерировано исключение типа SerializationException.
Если требуется сериализовать объект без учёта членов, имеющих несереализуемый тип, возможны несколько подходов:
- сделайте несериализуемый тип сериализуемым;
- если происходит автоматическая сериализация, декорируйте поля, которые не нужно сериализовать, атрибутом [NonSerialized];
- если происходит ручная сериализация, просто игнорируйте те члены, которые вам не нужны.
Стоит обратить внимание на тот факт, что атрибут [NonSerialized] применим только к полям. Таким образом, вы не сможете запретить сериализацию свойства, но, если оно будет иметь несериализуемый тип — получите исключение. Например, при попытке сериализации класса SerializedClass, определение которого приведено ниже:
Обойти эту ситуацию можно, реализовав свойство через поле, декорированное атрибутом [NonSerialized]:
Подобные ошибки, когда сериализуемый тип имеет члены несереализуемых типов, не декорированные атрибутом [NonSerialized], обнаруживает диагностическое правило V3097 статического анализатора кода PVS-Studio.
Напоминаю, что данное предупреждение не обязательно свидетельствует о наличии ошибки — всё зависит от используемого сериализатора.
Рассмотрим несколько примеров кода, в которых описанное условие было нарушено.
Предупреждение PVS-Studio: V3097 Possible exception: the ‘AkismetSpamService’ type marked by [Serializable] contains non-serializable members not marked by [NonSerialized]. Subtext.Framework AkismetSpamService.cs 31
Тип BlogUrlHelper поля _urlHelper не является сериализуемым, поэтому при попытке сериализации экземпляра класса AkismetSpamService некоторыми сериализаторами, будет сгенерировано исключение типа SerializationException. Решать проблему нужно, отталкиваясь от ситуации. Если используются сериализаторы типа BinaryFormatter или SoapFormatter — необходимо либо декорировать поле атрибутом [NonSerialized], либо декорировать атрибутом [Serializable] тип BlogUrlHepler. Если используются другие сериализаторы, не требующие наличия атрибута [Serializable] у сериализуемых полей, можно не забивать голову.
Предупреждение PVS-Studio: V3097 Possible exception: the ‘ResponsibleLegalPerson’ type marked by [Serializable] contains non-serializable members not marked by [NonSerialized]. NHibernate.Test ResponsibleLegalPerson.cs 9
Ситуация аналогична описанной выше — или пан, или пропал. Всё зависит от используемого сериализатора.
Не забывайте про атрибут [Serializable] при реализации интерфейса ISerializable
Данный совет относится скорее к тем, кто только начинает работать с сериализацией. Управляя сериализацией вручную, посредством реализации интерфейса ISerializable, легко забыть декорировать тип атрибутом [Serializable], что потенциально приводит к генерации исключения типа SerializationException. Такие сериализаторы, как BinaryFormatter, требуют наличия данного атрибута.
Интересные примеры данной ошибки встретились в проекте SharpDevelop.
Предупреждение PVS-Studio: V3096 Possible exception when serializing ‘SearchPatternException’ type. [Serializable] attribute is missing. ICSharpCode.AvalonEdit ISearchStrategy.cs 80
Предупреждение PVS-Studio: V3096 Possible exception when serializing ‘DecompilerException’ type. [Serializable] attribute is missing. ICSharpCode.Decompiler DecompilerException.cs 28
Для передачи объекта исключения между доменами приложений происходит его сериализация и десериализация. Соответственно, собственные типы исключений должны быть сериализуемыми. В приведённых выше примерах типы SearchPatternException и DecompilerException наследуются от Exception и реализуют конструкторы сериализации, но при этом не декорированы атрибутом [Serializable], а значит, что при попытке сериализации объектов данных типов (например, для передачи между доменами) будет сгенерировано исключение типа SerializationException. Таким образом, например, генерируя исключение в другом домене приложений, в текущем вы перехватите не сгенерированное исключение, а SerializationException.
Убедитесь, что в методе GetObjectData сериализуются все необходимые члены типа
Реализуя интерфейс ISerializable и определяя метод GetObjectData, вы берёте на себя ответственность за то, какие члены типа будут сериализованы и какие значения в них будут записаны. В этом случае для разработчиков открывается большой простор в управлении сериализацией: в качестве сериализуемого значения, ассоциированного с членом (а если быть более честным — с любой строкой) вы можете записать действительное значение сериализованного объекта, результат работы какого-либо метода, константное или литеральное значение — всё, что захотите.
Однако в данном случае на плечи разработчика ложится большая ответственность, потому что необходимо не забыть никакой член, подлежащий сериализации, даже если он находится в базовом классе. Все мы люди, так что иногда некоторые члены всё же забываются.
Для диагностирования таких ситуаций в статическом анализаторе кода PVS-Studio предусмотрено диагностическое правило V3099. Предлагаю ознакомиться с некоторыми примерами кода, обнаруженных данным правилом.
Предупреждение PVS-Studio: V3099 Not all the members of ‘XshdColor’ type are serialized inside ‘GetObjectData’ method: LineNumber, ColumnNumber. ICSharpCode.AvalonEdit XshdColor.cs 101
В этом коде нет проблем, описанных ранее, таких как неправильные модификаторы доступа у конструктора сериализации, отсутствие атрибута [Serializable] или модификатора virtual у метода GetObjectData.
Увы, ошибка здесь всё равно есть. В методе GetObjectData не учитываются свойства базового класса, а значит, при сериализации часть данных будет потеряна. В итоге, при десериализации будет восстановлен объект с другим состоянием.
В данном случае решением проблемы будет ручное добавление необходимых значений, например, таким образом:
Если бы базовый класс также реализовывал интерфейс ISerializable, решение было бы более элегантным — вызовом в производном методе GetObjectData базового.
Предупреждение PVS-Studio: V3099 Not all the members of ‘SessionImpl’ type are serialized inside ‘GetObjectData’ method: fetchProfile. NHibernate SessionImpl.cs 141
На этот раз забыли сериализовать поле текущего класса (fetchProfile). Как видно из определения, оно не декорировано атрибутом [NonSerialized] (в отличии от других полей, не сериализуемых в методе GetObjectData).
В данном проекте нашлось ещё два подобных места:
- V3099 Not all the members of ‘Configuration’ type are serialized inside ‘GetObjectData’ method: currentDocumentName, preMappingBuildProcessed. NHibernate Configuration.cs 127
- V3099 Not all the members of ‘ConnectionManager’ type are serialized inside ‘GetObjectData’ method: flushingFromDtcTransaction. NHibernate ConnectionManager.cs 290
С подобными ошибками связана интересная особенность — они либо приводят к генерации исключения, либо к трудноуловимым логическим ошибкам.
Исключение будет сгенерировано в том случае, если в конструкторе сериализации попытаются получить значение того поля, которое не было добавлено (обратятся по отсутствующему ключу). Если же про член забыли совсем (и в методе GetObjectData, и в конструкторе сериализации), будет происходить порча состояния объекта.
Обобщение
Заключение
Надеюсь, вы узнали что-то новое из статьи и стали большим экспертом в вопросах сериализации. Придерживаясь приведённых выше советов и правил, вы сможете тратить меньше времени на отладку, облегчить жизнь себе и другим разработчикам, работающим с вашими классами. А анализатор PVS-Studio ещё больше облегчит жизнь, позволяя выявлять подобные ошибки сразу после их появления.
Дополнительная информация
- V3094. Possible exception when deserializing type. The Ctor(SerializationInfo, StreamingContext) constructor is missing
- V3096. Possible exception when serializing type. [Serializable] attribute is missing
- V3097. Possible exception: type marked by [Serializable] contains non-serializable members not marked by [NonSerialized]
- V3099. Not all the members of type are serialized inside ‘GetObjectData’ method
- V3103. A private Ctor(SerializationInfo, StreamingContext) constructor in unsealed type will not be accessible when deserializing derived types
- V3104. ‘GetObjectData’ implementation in unsealed type is not virtual, incorrect serialization of derived type is possible
- MSDN. Serialization in the .NET Framework
- MSDN. Custom Serialization
Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Sergey Vasiliev. How to not shoot yourself in the foot when working with serialization.
Источник
- Remove From My Forums

Ошибка десериализации помоги разабраться
-
Общие обсуждения
-
Здравствуйте форумчани,
Пишу клиента для одной веб службу (*.wsdl) находящейся в интернете. Опыта в этих делах маловато у меня. Вот столкнулся с ошибкой десериализации. С начала я авторизовываюсь на сервисе, получаю тикет. Затем формирую посылку и отправляю. На что служба мне
выдает вот такой ответ:<?xml version="1.0" encoding="utf-8" ?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <SendProbeDataRequestResponse xmlns="http://probewebservices.traffic.com/schemas/v1_0/Probe" /> </soapenv:Body> </soapenv:Envelope>
На что дебагер ругается вот таким сообщением:
Ошибка десериализации тела ответного сообщения для операции «sendProbeData». Ожидался элемент End «Body» из пространства
имен «http://schemas.xmlsoap.org/soap/envelope/». Найден элемент «SendProbeDataRequestResponse» из пространства имен
«http://probewebservices.traffic.com/schemas/v1_0/Probe». Строка 1, позиция 322.
Как я понимаю при десериализации должно было вернутся вот такой ответ от службы согласно автосгенерированому коду по *.wsdl и .xsd<?xml version="1.0" encoding="utf-8" ?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> </soapenv:Body> </soapenv:Envelope>
//------------------------------------------------------------------------------ // <auto-generated> // Этот код создан программой. // Исполняемая версия:4.0.30319.235 // // Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае // повторной генерации кода. // </auto-generated> //------------------------------------------------------------------------------ namespace Navteq_NoJava_ConsoleApplication.NavteqProbe { [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] [System.ServiceModel.ServiceContractAttribute(Namespace="http://probewebservices.traffic.com/services/v1_0/Probe", ConfigurationName="NavteqProbe.ProbePortType")] public interface ProbePortType { // CODEGEN: Контракт генерации сообщений с операцией sendProbeData не является ни RPC, ни упакованным документом. [System.ServiceModel.OperationContractAttribute(IsOneWay=false, Action="http://probewebservices.traffic.com/sendProbeData")] [System.ServiceModel.FaultContractAttribute(typeof(string), Action="http://probewebservices.traffic.com/sendProbeData", Name="WebServiceFault", Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebService")] [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] void sendProbeData(Navteq_NoJava_ConsoleApplication.NavteqProbe.sendProbeData request); // CODEGEN: Контракт генерации сообщений с операцией pingProbe не является ни RPC, ни упакованным документом. [System.ServiceModel.OperationContractAttribute(Action="http://probewebservices.traffic.com/pingProbe", ReplyAction="*")] [System.ServiceModel.FaultContractAttribute(typeof(string), Action="http://probewebservices.traffic.com/pingProbe", Name="WebServiceFault", Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebService")] [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)] Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeResponse pingProbe(Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeRequest request); } /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebServiceClientContext")] public partial class WebServiceClientContext : object, System.ComponentModel.INotifyPropertyChanged { private string ticketField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=0)] public string ticket { get { return this.ticketField; } set { this.ticketField = value; this.RaisePropertyChanged("ticket"); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/Probe")] public partial class GeoLocation : object, System.ComponentModel.INotifyPropertyChanged { private float latitudeField; private float longitudeField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=0)] public float latitude { get { return this.latitudeField; } set { this.latitudeField = value; this.RaisePropertyChanged("latitude"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=1)] public float longitude { get { return this.longitudeField; } set { this.longitudeField = value; this.RaisePropertyChanged("longitude"); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/Probe")] public partial class GlobalPosition : object, System.ComponentModel.INotifyPropertyChanged { private GeoLocation geoLocField; private System.Nullable<int> headingField; private bool headingFieldSpecified; private System.Nullable<int> speedField; private bool speedFieldSpecified; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=0)] public GeoLocation geoLoc { get { return this.geoLocField; } set { this.geoLocField = value; this.RaisePropertyChanged("geoLoc"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=1)] public System.Nullable<int> heading { get { return this.headingField; } set { this.headingField = value; this.RaisePropertyChanged("heading"); } } /// <remarks/> [System.Xml.Serialization.XmlIgnoreAttribute()] public bool headingSpecified { get { return this.headingFieldSpecified; } set { this.headingFieldSpecified = value; this.RaisePropertyChanged("headingSpecified"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=2)] public System.Nullable<int> speed { get { return this.speedField; } set { this.speedField = value; this.RaisePropertyChanged("speed"); } } /// <remarks/> [System.Xml.Serialization.XmlIgnoreAttribute()] public bool speedSpecified { get { return this.speedFieldSpecified; } set { this.speedFieldSpecified = value; this.RaisePropertyChanged("speedSpecified"); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } /// <remarks/> [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.225")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/Probe")] public partial class ProbeData : object, System.ComponentModel.INotifyPropertyChanged { private string deviceAddressField; private string deviceTypeField; private string deviceSubTypeField; private string sourceIdField; private GlobalPosition globalPositionField; private System.DateTime collectionTimeGMTField; /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=0)] public string deviceAddress { get { return this.deviceAddressField; } set { this.deviceAddressField = value; this.RaisePropertyChanged("deviceAddress"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=1)] public string deviceType { get { return this.deviceTypeField; } set { this.deviceTypeField = value; this.RaisePropertyChanged("deviceType"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Order=2)] public string deviceSubType { get { return this.deviceSubTypeField; } set { this.deviceSubTypeField = value; this.RaisePropertyChanged("deviceSubType"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=3)] public string sourceId { get { return this.sourceIdField; } set { this.sourceIdField = value; this.RaisePropertyChanged("sourceId"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=4)] public GlobalPosition globalPosition { get { return this.globalPositionField; } set { this.globalPositionField = value; this.RaisePropertyChanged("globalPosition"); } } /// <remarks/> [System.Xml.Serialization.XmlElementAttribute(Order=5)] public System.DateTime collectionTimeGMT { get { return this.collectionTimeGMTField; } set { this.collectionTimeGMTField = value; this.RaisePropertyChanged("collectionTimeGMT"); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.ServiceModel.MessageContractAttribute(IsWrapped=false)] public partial class sendProbeData { [System.ServiceModel.MessageHeaderAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebServiceClientContext")] public Navteq_NoJava_ConsoleApplication.NavteqProbe.WebServiceClientContext WebServiceClientContextElement; [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/Probe", Order=0)] [System.Xml.Serialization.XmlArrayItemAttribute("probeData", IsNullable=false)] public ProbeData[] SendProbeDataRequest; public sendProbeData() { } public sendProbeData(Navteq_NoJava_ConsoleApplication.NavteqProbe.WebServiceClientContext WebServiceClientContextElement, ProbeData[] SendProbeDataRequest) { this.WebServiceClientContextElement = WebServiceClientContextElement; this.SendProbeDataRequest = SendProbeDataRequest; } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.ServiceModel.MessageContractAttribute(IsWrapped=false)] public partial class pingProbeRequest { [System.ServiceModel.MessageHeaderAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebServiceClientContext")] public Navteq_NoJava_ConsoleApplication.NavteqProbe.WebServiceClientContext WebServiceClientContextElement; [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebService", Order=0)] public string PlaceHolder; public pingProbeRequest() { } public pingProbeRequest(Navteq_NoJava_ConsoleApplication.NavteqProbe.WebServiceClientContext WebServiceClientContextElement, string PlaceHolder) { this.WebServiceClientContextElement = WebServiceClientContextElement; this.PlaceHolder = PlaceHolder; } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] [System.ServiceModel.MessageContractAttribute(IsWrapped=false)] public partial class pingProbeResponse { [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://probewebservices.traffic.com/schemas/v1_0/WebService", Order=0)] public string Ping; public pingProbeResponse() { } public pingProbeResponse(string Ping) { this.Ping = Ping; } } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public interface ProbePortTypeChannel : Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType, System.ServiceModel.IClientChannel { } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public partial class ProbePortTypeClient : System.ServiceModel.ClientBase<Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType>, Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType { public ProbePortTypeClient() { } public ProbePortTypeClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public ProbePortTypeClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public ProbePortTypeClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public ProbePortTypeClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] void Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType.sendProbeData(Navteq_NoJava_ConsoleApplication.NavteqProbe.sendProbeData request) { base.Channel.sendProbeData(request); } public void sendProbeData(Navteq_NoJava_ConsoleApplication.NavteqProbe.WebServiceClientContext WebServiceClientContextElement, ProbeData[] SendProbeDataRequest) { Navteq_NoJava_ConsoleApplication.NavteqProbe.sendProbeData inValue = new Navteq_NoJava_ConsoleApplication.NavteqProbe.sendProbeData(); inValue.WebServiceClientContextElement = WebServiceClientContextElement; inValue.SendProbeDataRequest = SendProbeDataRequest; ((Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType)(this)).sendProbeData(inValue); } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeResponse Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType.pingProbe(Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeRequest request) { return base.Channel.pingProbe(request); } public string pingProbe(Navteq_NoJava_ConsoleApplication.NavteqProbe.WebServiceClientContext WebServiceClientContextElement, string PlaceHolder) { Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeRequest inValue = new Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeRequest(); inValue.WebServiceClientContextElement = WebServiceClientContextElement; inValue.PlaceHolder = PlaceHolder; Navteq_NoJava_ConsoleApplication.NavteqProbe.pingProbeResponse retVal = ((Navteq_NoJava_ConsoleApplication.NavteqProbe.ProbePortType)(this)).pingProbe(inValue); return retVal.Ping; } } }
-
Изменен тип
11 июля 2011 г. 9:57
-
Изменен тип

. Да вроде ничего сложного, Items — будет массив классов:





