SoapFormatter и рекурсия, что сним просходит?
От: Lecha  
Дата: 20.09.10 09:01
Оценка:
Подскажите уважаемые, что просходит:
Задача сериализовать в соап некую древовоидную иерархию,
для примера набросал простенький тест:
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;

        }
    
    }
}


Вопросы к гурам: почему так????, Какие то еше решениая кроме, того что я уже нашел? Спасибо
Re: SoapFormatter и рекурсия, что сним просходит?
От: Пельмешко Россия blog
Дата: 20.09.10 09:19
Оценка: +1
Здравствуйте, Lecha, Вы писали:

L>Вопросы к гурам: почему так????


Я не гуру, но попробую ответить, ок?

Неужели Вы не догадывайтесь, что сериализатор не читает поток, а только пишет в него и не имеет ни малейшего представление что в нём уже есть.
Форматтер умеет сериализовывать целые графы объектов и нормально будет обрабатывать циклические ссылки только если будет иметь весь граф и "помнить" что он уже сериализовал.

L>Какие то еше решениая кроме, того что я уже нашел? Спасибо


Попробуйте всё же сериализовывать всё за один вызов Serialize()
Re[2]: SoapFormatter и рекурсия, что сним просходит?
От: Lecha  
Дата: 20.09.10 09:32
Оценка: :)
Здравствуйте, Пельмешко, Вы писали:
П>Я не гуру, но попробую ответить, ок?
похоже на то
П>Неужели Вы не догадывайтесь, что сериализатор не читает поток, а только пишет в него
рекомендую ознакомиться http://msdn.microsoft.com/en-us/library/wkyt1t1f.aspx
Исключение происxодит именно в Deserialize, пишет то он нормально
П>Попробуйте всё же сериализовывать всё за один вызов Serialize()
так про то и вопрос в том числе был, порекомендуйте более-менее еффективный метод сериализации дерева без рекурсии
Re[3]: SoapFormatter и рекурсия, что сним просходит?
От: Пельмешко Россия blog
Дата: 20.09.10 09:39
Оценка: +1
Здравствуйте, Lecha, Вы писали:

L>Здравствуйте, Пельмешко, Вы писали:

П>>Я не гуру, но попробую ответить, ок?
L>похоже на то



П>>Неужели Вы не догадывайтесь, что сериализатор не читает поток, а только пишет в него

L>рекомендую ознакомиться http://msdn.microsoft.com/en-us/library/wkyt1t1f.aspx

Это я Вам рекомендую с понятием сериализации вообще

П>>Попробуйте всё же сериализовывать всё за один вызов 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();
    }
}
Re: SoapFormatter и рекурсия, что сним просходит?
От: k.o. Россия  
Дата: 20.09.10 09:45
Оценка:
Здравствуйте, Lecha, Вы писали:

L>Подскажите уважаемые, что просходит:

L>Задача сериализовать в соап некую древовоидную иерархию,
L>для примера набросал простенький тест:

L>Вопросы к гурам: почему так????, Какие то еше решениая кроме, того что я уже нашел? Спасибо


Потому что каждый новый форматтер создаёт новую структуру xml, и ты все эти xml засовываешь в один файл:

<SOAP-ENV:Envelope ...>
...
</SOAP-ENV:Envelope>
<SOAP-ENV:Envelope ...>
...
</SOAP-ENV:Envelope>
<SOAP-ENV:Envelope ...>
...
</SOAP-ENV:Envelope>


Десериализатор же ожидает, что ему на вход подадут корректный xml.

А зачем вобще самому по иерархии ходить? Сериализатор сам с этим справляется.
Re[4]: SoapFormatter и рекурсия, что сним просходит?
От: Lecha  
Дата: 20.09.10 10:07
Оценка:
Здравствуйте, Пельмешко, Вы писали:
П>Это я Вам рекомендую с понятием сериализации вообще
Ну слава богу что кому то помог с сериализацией и чтением разобраться

Ну и пытаемся читать:
я где то писал, что это рельный код и надо заставить работать именно этот пример?
Напротив я писал:"для примера набросал простенький тест", т. сказать чтоб проблему проиллюстрировать

П>Вы не поверите!

Для молодых и горячих рекомендую повторить тоже с:
[Serializable]
class A
{
  public string val;
  public List<A> children = new List<A>();
}


вопросы правда остаются:
-почему так с форматтером?
-порекомендуйте более-менее еффективный метод сериализации дерева без рекурсии
Re[2]: SoapFormatter и рекурсия, что сним просходит?
От: Lecha  
Дата: 20.09.10 10:22
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Десериализатор же ожидает, что ему на вход подадут корректный xml.

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

KO>А зачем вобще самому по иерархии ходить? Сериализатор сам с этим справляется.


Пример только чтоб показать где проблема, в реальном коде что-то типа этого:
[Serializable]
class A
{
  public string val;
  public List<A> children = new List<A>();
}
Re[3]: SoapFormatter и рекурсия, что сним просходит?
От: k.o. Россия  
Дата: 20.09.10 10:54
Оценка:
Здравствуйте, Lecha, Вы писали:

L>Здравствуйте, k.o., Вы писали:


KO>>Десериализатор же ожидает, что ему на вход подадут корректный xml.

L>так я и спрашиваю чем он некорректный? и почему десериализациай с одной инстанцией работает, а со многими нет, хотя поток тот же и позиции те же.

Это я поторопился. SoapFormatter предназначен для работы с потоком SOAP сообщений, поэтому он пытется десериализовать всё, что может прочитать из потока и если передавать ему один и тот же поток несколько раз он будет использовать уже прочитанные ранее сообщения. Соответсвенно, все последующие форматтеры, которые ты создаёшь при десериализации пытаются читать данные из потока, с позиции в конце файла, а первый форматтер работает, потому что он эти данные уже прочитал.
Re[4]: SoapFormatter и рекурсия, что сним просходит?
От: Lecha  
Дата: 20.09.10 11:12
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Здравствуйте, Lecha, Вы писали:


L>>Здравствуйте, k.o., Вы писали:


KO>>>Десериализатор же ожидает, что ему на вход подадут корректный xml.

L>>так я и спрашиваю чем он некорректный? и почему десериализациай с одной инстанцией работает, а со многими нет, хотя поток тот же и позиции те же.

KO>Это я поторопился. SoapFormatter предназначен для работы с потоком SOAP сообщений, поэтому он пытется десериализовать всё, что может прочитать из потока и если передавать ему один и тот же поток несколько раз он будет использовать уже прочитанные ранее сообщения. Соответсвенно, все последующие форматтеры, которые ты создаёшь при десериализации пытаются читать данные из потока, с позиции в конце файла, а первый форматтер работает, потому что он эти данные уже прочитал.


Спасибо, интересно, это многое обьясняет, а где бы про это почитать? Какой размер буфера куда он зачитывает все при первом обрашнии к потоку? Можно ли на него как то влиять? Где он позицию в этом буфере держит и т.д?
Re[5]: SoapFormatter и рекурсия, что сним просходит?
От: Пельмешко Россия blog
Дата: 20.09.10 11:41
Оценка:
Здравствуйте, 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;
    }
}

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