Подскажите уважаемые, что просходит:
Задача сериализовать в соап некую древовоидную иерархию,
для примера набросал простенький тест:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
using System.IO;
namespace TestSoap
{
class Program
{
static void Main(string[] args)
{
A root = new A();
root.val = "root";
A child = new A();
child.val = "child";
A childchild = new A();
childchild.val = "childchild";
child.children.Add(childchild);
root.children.Add(child);
FileStream fsw = File.Create("test.soap");
A.SerializeSoap(fsw, root);
fsw.Close();
FileStream fsr = File.OpenRead("test.soap");
A readed= A.DeSerializeSoap(fsr);
fsr.Close();
Console.WriteLine("OK");
Console.ReadKey();
}
}
[Serializable]
class A
{
public string val;
public ArrayList children = new ArrayList();
public static void SerializeSoap(Stream stream, A a)
{
SoapFormatter sf = new SoapFormatter();
sf.Serialize(stream,a);
foreach (A child in a.children)
{
SerializeSoap(stream, child);
}
}
public static A DeSerializeSoap(Stream stream)
{
SoapFormatter sf = new SoapFormatter();
A a= sf.Deserialize(stream) as A;
ArrayList tmpchildren = new ArrayList();
foreach (A child in a.children)
{
A readedchild= DeSerializeSoap(stream) as A;
tmpchildren.Add(readedchild);
}
a.children = tmpchildren;
return a;
}
}
}
В рекурсиии SoapFormatter бросает исключение, что совершенно мне непонятно, поток то тот же, позиция в нем тоже.
Экспериментально нашел что не нравится ему ресоздание SoapFormatter каждый раз, вкартце вот так все работает:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
using System.IO;
namespace TestSoap
{
class Program
{
static void Main(string[] args)
{
A root = new A();
root.val = "root";
A child = new A();
child.val = "child";
A childchild = new A();
childchild.val = "childchild";
child.children.Add(childchild);
root.children.Add(child);
FileStream fsw = File.Create("test.soap");
A.SerializeSoap(fsw, root);
fsw.Close();
FileStream fsr = File.OpenRead("test.soap");
A readed= A.DeSerializeSoap(fsr,null);
fsr.Close();
Console.WriteLine("OK");
Console.ReadKey();
}
}
[Serializable]
class A
{
public string val;
public ArrayList children = new ArrayList();
public static void SerializeSoap(Stream stream, A a)
{
SoapFormatter sf = new SoapFormatter();
sf.Serialize(stream,a);
foreach (A child in a.children)
{
SerializeSoap(stream, child);
}
}
public static A DeSerializeSoap(Stream stream, SoapFormatter sf)
{
if (sf == null)
{
sf = new SoapFormatter();
}
A a= sf.Deserialize(stream) as A;
ArrayList tmpchildren = new ArrayList();
foreach (A child in a.children)
{
A readedchild= DeSerializeSoap(stream,sf) as A;
tmpchildren.Add(readedchild);
}
a.children = tmpchildren;
return a;
}
}
}
Вопросы к гурам: почему так????, Какие то еше решениая кроме, того что я уже нашел? Спасибо
Здравствуйте, Lecha, Вы писали:
L>Вопросы к гурам: почему так????
Я не гуру, но попробую ответить, ок?
Неужели Вы не догадывайтесь, что сериализатор не читает поток, а только пишет в него и не имеет ни малейшего представление что в нём уже есть.
Форматтер умеет сериализовывать целые графы объектов и нормально будет обрабатывать циклические ссылки только если будет иметь весь граф и "помнить" что он уже сериализовал.
L>Какие то еше решениая кроме, того что я уже нашел? Спасибо
Попробуйте всё же сериализовывать всё за один вызов Serialize()
Re[2]: SoapFormatter и рекурсия, что сним просходит?
Здравствуйте, Пельмешко, Вы писали: П>Я не гуру, но попробую ответить, ок?
похоже на то П>Неужели Вы не догадывайтесь, что сериализатор не читает поток, а только пишет в него
рекомендую ознакомиться http://msdn.microsoft.com/en-us/library/wkyt1t1f.aspx
Исключение происxодит именно в Deserialize, пишет то он нормально П>Попробуйте всё же сериализовывать всё за один вызов Serialize()
так про то и вопрос в том числе был, порекомендуйте более-менее еффективный метод сериализации дерева без рекурсии
Re[3]: SoapFormatter и рекурсия, что сним просходит?
Это я Вам рекомендую с понятием сериализации вообще
П>>Попробуйте всё же сериализовывать всё за один вызов Serialize() L>так про то и вопрос в том числе был, порекомендуйте более-менее еффективный метод сериализации дерева без рекурсии
Вы не поверите!
namespace TestSoap
{
class Program
{
static void Main(string[] args)
{
A root = new A();
root.val = "root";
A child = new A();
child.val = "child";
A childchild = new A();
childchild.val = "childchild";
child.children.Add(childchild);
root.children.Add(child);
var serializer = new SoapFormatter();
using (var fsw = File.Create("test.soap"))
{
serializer.Serialize(fsw, root);
}
using (var fsr = File.OpenRead("test.soap"))
{
var readed = (A) serializer.Deserialize(fsr);
}
Console.WriteLine("OK");
Console.ReadKey();
}
}
[Serializable]
class A
{
public string val;
public ArrayList children = new ArrayList();
}
}
Здравствуйте, Lecha, Вы писали:
L>Подскажите уважаемые, что просходит: L>Задача сериализовать в соап некую древовоидную иерархию, L>для примера набросал простенький тест:
L>Вопросы к гурам: почему так????, Какие то еше решениая кроме, того что я уже нашел? Спасибо
Потому что каждый новый форматтер создаёт новую структуру xml, и ты все эти xml засовываешь в один файл:
Здравствуйте, Пельмешко, Вы писали: П>Это я Вам рекомендую с понятием сериализации вообще
Ну слава богу что кому то помог с сериализацией и чтением разобраться
Ну и пытаемся читать:
я где то писал, что это рельный код и надо заставить работать именно этот пример?
Напротив я писал:"для примера набросал простенький тест", т. сказать чтоб проблему проиллюстрировать
П>Вы не поверите!
Для молодых и горячих рекомендую повторить тоже с:
[Serializable]
class A
{
public string val;
public List<A> children = new List<A>();
}
вопросы правда остаются:
-почему так с форматтером?
-порекомендуйте более-менее еффективный метод сериализации дерева без рекурсии
Re[2]: SoapFormatter и рекурсия, что сним просходит?
Здравствуйте, k.o., Вы писали:
KO>Десериализатор же ожидает, что ему на вход подадут корректный xml.
так я и спрашиваю чем он некорректный? и почему десериализациай с одной инстанцией работает, а со многими нет, хотя поток тот же и позиции те же.
KO>А зачем вобще самому по иерархии ходить? Сериализатор сам с этим справляется.
Пример только чтоб показать где проблема, в реальном коде что-то типа этого:
[Serializable]
class A
{
public string val;
public List<A> children = new List<A>();
}
Re[3]: SoapFormatter и рекурсия, что сним просходит?
Здравствуйте, Lecha, Вы писали:
L>Здравствуйте, k.o., Вы писали:
KO>>Десериализатор же ожидает, что ему на вход подадут корректный xml. L>так я и спрашиваю чем он некорректный? и почему десериализациай с одной инстанцией работает, а со многими нет, хотя поток тот же и позиции те же.
Это я поторопился. SoapFormatter предназначен для работы с потоком SOAP сообщений, поэтому он пытется десериализовать всё, что может прочитать из потока и если передавать ему один и тот же поток несколько раз он будет использовать уже прочитанные ранее сообщения. Соответсвенно, все последующие форматтеры, которые ты создаёшь при десериализации пытаются читать данные из потока, с позиции в конце файла, а первый форматтер работает, потому что он эти данные уже прочитал.
Re[4]: SoapFormatter и рекурсия, что сним просходит?
Здравствуйте, k.o., Вы писали:
KO>Здравствуйте, Lecha, Вы писали:
L>>Здравствуйте, k.o., Вы писали:
KO>>>Десериализатор же ожидает, что ему на вход подадут корректный xml. L>>так я и спрашиваю чем он некорректный? и почему десериализациай с одной инстанцией работает, а со многими нет, хотя поток тот же и позиции те же.
KO>Это я поторопился. SoapFormatter предназначен для работы с потоком SOAP сообщений, поэтому он пытется десериализовать всё, что может прочитать из потока и если передавать ему один и тот же поток несколько раз он будет использовать уже прочитанные ранее сообщения. Соответсвенно, все последующие форматтеры, которые ты создаёшь при десериализации пытаются читать данные из потока, с позиции в конце файла, а первый форматтер работает, потому что он эти данные уже прочитал.
Спасибо, интересно, это многое обьясняет, а где бы про это почитать? Какой размер буфера куда он зачитывает все при первом обрашнии к потоку? Можно ли на него как то влиять? Где он позицию в этом буфере держит и т.д?
Re[5]: SoapFormatter и рекурсия, что сним просходит?
Здравствуйте, Lecha, Вы писали:
L>я где то писал, что это рельный код и надо заставить работать именно этот пример?
А очень жаль, что Вы спрашиваете не то, что Вам реально надо решить
L>Напротив я писал:"для примера набросал простенький тест", т. сказать чтоб проблему проиллюстрировать
Давайте по чесноку, Ваша проблема — отсутствие поддержки обобщённых типов и коллекций soap-сериализатором, а не проблема разруливания ссылок сериализатором при множественных вызовах Deserialize().
П>>Вы не поверите! L>Для молодых и горячих рекомендую повторить тоже с: L>
L>[Serializable]
L>class A
L>{
L> public string val;
L> public List<A> children = new List<A>();
L>}
L>
L>-порекомендуйте более-менее еффективный метод сериализации дерева без рекурсии
Проблему с generic-типами наименее болезненно можно обойти двумя способами:
Не использовать обобщённые типы вовсе
Использовать кастомную сериализацию.
У SOAP-форматтера не очень хорошо обстоят дела, если объект реализует ISerializable и ссылается на объекты, находящиеся далее по потоку...
Однако работает метод, применяемый в конкурентных коллекциях .NET 4.0:
[Serializable]
class A
{
[NonSerialized]
public List<A> children = new List<A>();
public string val;
private A[] serializeArray;
[OnDeserialized]
void OnDeserialized(StreamingContext _) {
this.children = this.serializeArray.ToList();
this.serializeArray = null;
}
[OnSerializing]
void OnSerializing(StreamingContext _) {
this.serializeArray = this.children.ToArray();
}
[OnSerialized]
void OnSerialized(StreamingContext _) {
this.serializeArray = null;
}
}
Да, это мягко говоря не очень приятно выглядит, но позволит Вам пользоваться сериализатором как положено за один вызов и правильно десериализовывать граф + данный подход менее подвержен ошибкам. Не надо дублировать функционал форматтера, который сам умеет бегать по полям и коллекциям, Вы решаете не ту проблему.