Stránka 1 z 1

Serializace

Napsal: 10 srp 2012 16:04
od Leny-m
Kdyby nekdo bojoval se serializaci, tak tady je pekny navod, ze ktereho jsem se to ted naucil:

http://www.switchonthecode.com/tutorial ... -to-a-file

K cemu je serializace dobra? Umozni mi do souboru ulozit rovnou cely objekt. Priklad: chci si nekam zaznamenat List<Waypoint>, ktery obsahuje udaje o souradnich, nasledujicich a predchozich waypointech. Nemuzu ho jentak ulozit do souboru, protoze bych pak mel hodne starosti s nacitanim. Musel bych totiz data ukladat jako string a v urcitem formatu, abych je zpetne dokazal nacist. Tak jsem to alespon pochopil :p Takze jsem si nasel tenhle navod a podle nej mohu serializovat tridu pro waypoint a pak pro samotny list waypointu. Diky tomu jsem schopny ulozit cely list do souboru a zpetne ho nacist a rovnou pouzivat.

Re: Serializace

Napsal: 11 srp 2012 15:25
od darthdeus
V principu serializace = uložení objektu do stringu/binární podoby, aby to šlo pak zpětně deserializovat.

Můžeš např. vzít pole waypointů, serializovat je do souboru, poslat někomu, ten to ve svým scriptu deserializuje a má zpátky původní pole. Nic víc, nic míň :)

Smysl to má pokud např. máš nějaký runtime nastavení nebo hodnoty, např. něco co si naklikáš ve hře, a chceš aby ti to zůstalo i po tom co tu app vypneš.

Re: Serializace

Napsal: 12 srp 2012 02:19
od Leny-m
Resim problem ohledne serializace, snad. Pridam si par waypointu, ulozim si je do souboru, zkusim je ze souboru nacist - ok. Relognu. Zkusim waypointy nacist ze souboru a dostanu: "Error: Runtime error: Sestavení gout1_1b, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null nebylo nale". Kdyz to zkusim jeste jednou potom loadnout, tak to vypise chybu o pretypovani. Takova tam ale neni, jinak by to neslo spustit napoprve.

E: cela chyba

Kód: Vybrat vše

02:26 Phoenix: System.Runtime.Serialization.SerializationException: Sestavení f_6uuj8g, Version=0.0.0.0, Culture=ne
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assembly
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memb
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.ObjectMap.Create(String name, String[] memberNam
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObje
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHead
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, 
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serialization
02:26 Phoenix:    v System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serialization
02:26 Phoenix:    v Phoenix.Scripts.Serializer.DeSerializeObject(String filename)
E2: Chapu, ze je to asi problem ruznych assembly, zkusil jsem pouzit neco generickeho pres

Kód: Vybrat vše

bFormatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
, ale to nejak neklaplo. Zkousel jsem Binder, ale to mi take nepomohlo. Z toho, co ctu je nejspis problem, ze pouzivam svuj vlastni list s vlastnimi objekty.

E3: ok, assembly format pro serializaci a deserializaci mam nastaveny na simple, binder mam upraveny tak, ze dokaze najit spravnou assembly a jeji typ sikovne podstrcit dal, ale narazil jsem na problem s pretypovanim, ktery to hlasi... asi jelikoz je rozdil v assembly, tak to neni schopne castnout :/

Re: Serializace

Napsal: 12 srp 2012 13:48
od Carda
BinaryFormatter ukládá přesný info o assembly která serializaci prováděla a při deserializaci zase tu přesně stejnou assembly hledá. Skripty se ti pokaždé kompilujou znova -> není to úplně ta samá assembly -> nebude ti to fungovat. Netušim jestli se to dá nějak obejít, asi jo, ale ne jednoduše.
Každopádně bych řek, že s tym budeš mít takdle akorát zbytečný sraní a bude to zbytečně složitý :-) Navíc ten BinaryFormatter není zrovna nejrychlejší.

Kód: Vybrat vše

//Save
List<WayPoint> wayPoints = new List<WayPoint>();
using (BinaryWriter writer = new BinaryWriter(File.Open("WayPoints", FileMode.Create, FileAccess.Write)))
    foreach (WayPoint wayPoint in wayPoints)
    {
        writer.Write(wayPoint.X);
        writer.Write(wayPoint.Y);
        writer.Write(wayPoint.Z);
    }

//Load
List<WayPoint> wayPoints = new List<WayPoint>();
using (BinaryReader reader = new BinaryReader(File.Open("WayPoints", FileMode.Open, FileAccess.Read)))
    while (reader.PeekChar() != -1)
        wayPoints.Add(new WayPoint(reader.ReadUInt16(), reader.ReadUInt16(), reader.ReadUInt16()));
Nejjednoduší asi :-) Nebo prostě do XML, přes System.Xml.Linq (XDocument, XElement, ...) je to podobně jednoduchej kód. Záleží co ukládáš a co s tym chceš dělat, že jo. Jestli chceš mít možnost to měnit i bez spuštěnýho programu přímo v souboru tak XML (třeba abys kvůli jednomu waypointu nemusel nahrávat všechno znova od začátku), jestli je to jedno tak první možnost no.

Re: Serializace

Napsal: 12 srp 2012 15:24
od Leny-m
Jsem ve stavu, kdy dokazi identifikovat assembly, ve ktere bylo serializovano, ale nejsem schopny ji nacist tak, aby nedoslo k jejimu zdvojeni, proto mi to pak nejde pretypovat, protoze ackoliv jsou ty assembly stejne, nejsou identicke. Mozna se to tyka LoadFrom a LoadFile metod a vytvareni instanci assembly.

XML si nechavam az jako zalohu, tohle mi prislo jako docela zajimavy problem, tak jsem se do toho zakousnul :) Dik za kod, dost mozna u neceho podobneho skoncim. By me nenapadlo, ze to bude tak pomotane :)

Kód: Vybrat vše

    sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
           Type typeToDeserialize = null;    
           try
           {
                string ToAssemblyName = assemblyName.Split(',')[0];   
                //UO.Print("co hledam: {0}",ToAssemblyName);
                Assembly[] Assemblies = AppDomain.CurrentDomain.GetAssemblies();    
                foreach (Assembly ass in Assemblies)
                {
                    //UO.Print(ass.FullName.Split(',')[0]);
                    if (ass.FullName.Split(',')[0] == ToAssemblyName)
                    {
                        typeToDeserialize = ass.GetType(typeName);
                        break;    
                    }    
                }    
           } catch (System.Exception exception)
           {
             throw exception;    
           }
    
           return typeToDeserialize;    
        }
    }
Jdu na tu Tvou alternativu, tohle me fakt zklamalo :) Akorat je problem, ze ja neukladam jen x, y, z, ale i prev a next Waypoint ^^ Bude to vypadat asi trochu strasidelne.

E: aby peekChar nevyhazoval chybu, musel jsem upravit tohle:

Kód: Vybrat vše

using (BinaryReader reader = new BinaryReader(new System.IO.FileStream(path, FileMode.Open, FileAccess.Read),Encoding.ASCII))
E2: Vse jiz funguje, jen me mrzi, ze to neni presne to reseni, ktere jsem chtel :) Diky za pomoc ;)

Re: Serializace

Napsal: 12 srp 2012 16:43
od Carda
Když už sis s tym takdle vyhrál.
V tom kódu cos postnul - neměl bys brat ten Type z assembly co právě používáš než z té ve které si to serializoval?
Tzn. prostě

Kód: Vybrat vše

public override Type BindToType(string assemblyName, string typeName)
...
Assembly assembly = Assembly.GetExecutingAssembly();
typeToDeserialize = assembly.GetType(typeName);
...
Tim co si udělal najdeš ten původní Type (+ teda jen dokavaď to celý nevypneš) a ten samozřejmě nejde přetypovat na ten Type co máš teď nově zkompilovanej.

Re: Serializace

Napsal: 12 srp 2012 19:29
od Leny-m
Zkusil jsem to a myslim, ze to ani fungovat nemuze, nebo jsem Ti nerozumel.

Kód: Vybrat vše

    sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
           Type typeToDeserialize = null;    
           try
           {
                Assembly assembly = Assembly.GetExecutingAssembly();
                typeToDeserialize = assembly.GetType(typeName);
           } catch (System.Exception exception)
           {
             throw exception;    
           }
    
           return typeToDeserialize;    
        }
    }
Nelze pretypovat. Neco podobneho jsem zkousel driv, se stejnym vysledkem.

Kód: Vybrat vše

 string currentAssembly = Assembly.GetExecutingAssembly().FullName;
typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, currentAssembly));
Je pravda, ze do predminuleho tydne jsem Cecko nevidel, takze je dost mozne, ze tohle vsechno je zpusobene jen mou nezkusenosti. Uz jenom to, ze jsem nepouzil rovnou XMLSerializer, ktery by mi podle me dost usnadnil praci. Alespon se to tak tvari: http://mfharoon.blogspot.cz/2006/12/usi ... e-not.html

Re: Serializace

Napsal: 12 srp 2012 21:33
od Carda

Kód: Vybrat vše

    public class CoolBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            int index = typeName.IndexOf(", Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
            if (index != -1)
            {
                typeName = typeName.Remove(index - 8, 8);
                typeName = typeName.Insert(index - 8, typeof(CoolBinder).Assembly.GetName().Name);
            }
            return Type.GetType(typeName);
        }
    }
:-)
Todle i funguje, jen to není úplně čistá cesta no :-) Ale nevim jak jinak zjistit jméno tej původní assembly...

Re: Serializace

Napsal: 12 srp 2012 21:37
od Leny-m
Jmeno puvodni assembly je v assemblyName, to neni problem zjistit:

Kód: Vybrat vše

public override Type BindToType(string assemblyName, string typeName)
...
string ToAssemblyName = assemblyName.Split(',')[0];
Ale ziskat pak odkaz na tu puvodni assembly mi prijde lehce imposibru.

Re: Serializace

Napsal: 12 srp 2012 23:34
od Carda
Když deserializuješ List<WayPoint>, tak se poprvé BindToType volá právě pro ten list - assemblyname je "mscorlib, blablabla" (tzn ne jméno původní assembly, ale assembly ve které je ten list), typename je zhruba "system.blabla.list[blablabla.WayPoint[tadytoděsnýinfoopůvodníassembly]]" -> musíš v tom typename najít jméno tej původní assembly, vyměnit za tu aktuální (tak jak sem to udělal vejš)
Až podruhé se to volá pro ten samotnej waypoint, tam už je v assemblyname ta původní assembly, to ti je ale už k ničemu.
To co sem hodil vejš funguje, jen to nevypadá úplně pěkně :-)

Re: Serializace

Napsal: 13 srp 2012 02:00
od Leny-m
Carda píše:Když deserializuješ List<WayPoint>, tak se poprvé BindToType volá právě pro ten list - assemblyname je "mscorlib, blablabla" (tzn ne jméno původní assembly, ale assembly ve které je ten list), typename je zhruba "system.blabla.list[blablabla.WayPoint[tadytoděsnýinfoopůvodníassembly]]" -> musíš v tom typename najít jméno tej původní assembly, vyměnit za tu aktuální (tak jak sem to udělal vejš)
Až podruhé se to volá pro ten samotnej waypoint, tam už je v assemblyname ta původní assembly, to ti je ale už k ničemu.
To co sem hodil vejš funguje, jen to nevypadá úplně pěkně :-)
Diky za vysvetleni, ted uz tomu rozumim. A dik za kod :)

Re: Serializace

Napsal: 13 srp 2012 11:28
od Carda
Takdle je to asi lepší :-)

Kód: Vybrat vše

    public class CoolBinder : SerializationBinder
    {
        //při serializaci změnit jméno assembly na vlastní
        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            assemblyName = serializedType.Assembly.FullName;
            typeName = serializedType.FullName.Replace(typeof(CoolBinder).Assembly.GetName().Name, "ScriptAssembly");
        }

        //při deserializaci změnit vlastní jméno zpátky na aktuální
        public override Type BindToType(string assemblyName, string typeName)
        {
            typeName = typeName.Replace("ScriptAssembly", typeof(CoolBinder).Assembly.GetName().Name);
            return Type.GetType(typeName);
        }
    }

Re: Serializace

Napsal: 13 srp 2012 20:09
od Leny-m
To uz vypada mnohem lepe :) Super!