public void Step7_GameDatabase() { /* * Scenario: * We have "MyMonster" and "MyAbility" for a game. * We want to be able to easily serialize the whole graph, but we also * want MyMonster and MyAbility instances to be saved in their own files! * * Lets first take a look at the classes we're working with: */ MyMonster monster = new MyMonster(); monster.Name = "Skeleton Mage"; monster.Health = 250; monster.Mana = 100; monster.Abilities.Add(new MyAbility { Name = "Fireball", ManaCost = 12, Cooldown = 0.5f, }); monster.Abilities.Add(new MyAbility { Name = "Ice Lance", ManaCost = 14, Cooldown = 6, }); // We want to save monsters and abilities in their their own files. // Using other serializers this would be a terribly time-consuming task. // We would have to add attributes or maybe even write custom serializers so the "root objects" // can be when they are referenced in another object.. // Then we'd need a separate field maybe where we'd save a list of IDs or something.... // And then at load(deserialization)-time we would have to manually load that list, and resolve the // objects they stand for... // // And all that for literally every "foreign key" (as it is called in database terms). :puke: ! // // // Ceras offers a much better approach. // You can implement IExternalRootObject, telling Ceras the "Id" of your object. // You can generate that Id however you want, most people would proably opt to use some kind of auto-increment counter // from their SQLite/SQL/MongoDB/LiteDB/... // // At load time Ceras will ask you to load the object again given its Id. // SerializerConfig config = new SerializerConfig(); var myGameObjectsResolver = new MyGameObjectsResolver(); config.ExternalObjectResolver = myGameObjectsResolver; config.KnownTypes.Add(typeof(MyAbility)); config.KnownTypes.Add(typeof(MyMonster)); config.KnownTypes.Add(typeof(List <>)); // Ceras will call "OnExternalObject" (if you provide a function). // It can be used to find all the IExternalRootObject's that Ceras encounters while // serializing your object. // // In this example we just collect them in a list and then serialize them as well List <IExternalRootObject> externalObjects = new List <IExternalRootObject>(); config.OnExternalObject = obj => { externalObjects.Add(obj); }; var serializer = new CerasSerializer(config); myGameObjectsResolver.Serializer = serializer; var monsterData = serializer.Serialize(monster); // we can write this monster to the "monsters" sql-table now monsterData.VisualizePrint("Monster data"); MyGameDatabase.Monsters[monster.Id] = monsterData; // While serializing the monster we found some other external objects as well (the abilities) // Since we have collected them into a list we can serialize them as well. // Note: while in this example the abilities themselves don't reference any other external objects, // it is quite common in a real-world scenario that every object has tons of references, so keep in mind that // the following serializations would keep adding objects to our 'externalObjects' list. for (var i = 0; i < externalObjects.Count; i++) { var obj = externalObjects[i]; var abilityData = serializer.Serialize(obj); var id = obj.GetReferenceId(); MyGameDatabase.Abilities[id] = abilityData; abilityData.VisualizePrint($"Ability {id} data:"); } // Problems: /* * 1.) * Cannot deserialize recursively * we'd overwrite our object cache, Ids would go out of order, ... * Example: A nested object tells us "yea, this is object ID 5 again", while 5 is already some other object (because its the wrong context!) * * -> Need to make it so the serializer has Stack<>s of object- and type-caches. * * * 2.) * Keep in mind that we can NOT share a deserialization buffer!! * If we load from Monster1.bin, and then require Spell5.bin, that'd overwrite our shared buffer, * and then when the spell is done and we want to continue with the monster, the data will have changed! * * -> debug helper: "The data has changed while deserializing, this must be a bug on your end!" * * 3.) * while deserializing objects, we need to create them, add to cache, then populate. * otherwise we might get into a situation where we want to load an ability that points to a monster (the one we're already loading) * and then we end up with two monsters (and if they code continues to run, infinite, and we get a stackoverflow) * In other words: Objects that are still being deserialized, need to already be in the cache, so they can be used by other stuff! * * -> create helper class that deals with deserializing object graphs? * */ // Load the data again: var loadedMonster = serializer.Deserialize <MyMonster>(MyGameDatabase.Monsters[1]); var ability1 = serializer.Deserialize <MyAbility>(MyGameDatabase.Abilities[1]); var ability2 = serializer.Deserialize <MyAbility>(MyGameDatabase.Abilities[2]); }
public void Step3_Recycling() { /* * Scenario: * - You might be developing a game or other timing-sensitive application * where performance is extremely important and where small stutters by the GC have to be avoided * - You have your own object-pool and want Ceras to obtain instances from it and return them to it. * * What you want: * - You want more performance * - You want to decrease GC pauses and GC pressure * * What can we do: * - Recycle objects, reducing GC pressure * - Take objects from a pool instead of doing "new MyThing()" to improve cache-coherence. */ // Assuming we're receiving "network packets" over the internet, we'd instantiate a new object every time we call Deserialize // That is pretty wasteful. We receive one object, deserialize it, use it, then discard it, ... // // What we can do is have one instance that we can just overwrite all the time! // // Ceras will use the provided object and overwrite the fields; instead of creating a new object instance. // If no instance is provided, Ceras will just instantiate one. // // Hint: This can also be used to quickly set or reset an object to some predefined values in other scenarios. var serializer = new CerasSerializer(); Person recycledPerson = new Person { Health = 35, Name = "test" }; byte[] buffer = serializer.Serialize(recycledPerson); // Lets assume we already got a network buffer and now we just have to read it. for (int i = 0; i < 100; i++) { // No person object will be allocated, the fields // of 'recycledPerson' will just be overwritten serializer.Deserialize <Person>(ref recycledPerson, buffer); } // // Now we'll use some extremely simple object-pooling solution to reduce GC pressure. // The 'MyVerySimplePool' is obviously just for illustration, in something like Unity3D you would // of course make something much more elaborate... // // If the data in the buffer tells us "it's a 'null' object" then Ceras will of course set 'recycledPerson' to null. // But now you might wonder what happens to the instance that was previously inside 'recycledPerson'. // Normally the answer would be that the object would be simply lost (and eventually its space would be reclaimed by the .NET "garbage-collector"). // // In some scenarios (games) we don't want this because garbage-collections often cause stutters. // A common solution to this is object-pooling. // Ceras supports that by allowing you to "catch" unused objects, so you can return them to your object-pool. MyVerySimplePool <Person> pool = new MyVerySimplePool <Person>(); SerializerConfig config = new SerializerConfig(); config.ObjectFactoryMethod = type => { if (type != typeof(Person)) { return(null); } return(pool.GetFromPool()); }; config.DiscardObjectMethod = obj => { pool.ReturnToPool((Person)obj); }; serializer = new CerasSerializer(config); // todo: the example is not fully done yet /* * var personA = new Person { Name = "a", Health = 1 }; * var personB = new Person { Name = "b", Health = 2 }; * var personC = new Person { Name = "c", Health = 3 }; * * personA.BestFriend = personB; * personB.BestFriend = personC; * personC.BestFriend = personA; * * serializer.Serialize(); */ }
protected internal virtual void FillSerializers(XmlNode node, SerializationConfig serializationConfig) { foreach (XmlNode child in node.ChildNodes) { string name = CleanNodeName(child); string value = GetTextContent(child); if ("serializer".Equals(name)) { var serializerConfig = new SerializerConfig(); serializerConfig.SetClassName(value); string typeClassName = GetAttribute(child, "type-class"); serializerConfig.SetTypeClassName(typeClassName); serializationConfig.AddSerializerConfig(serializerConfig); } else { if ("global-serializer".Equals(name)) { var globalSerializerConfig = new GlobalSerializerConfig(); globalSerializerConfig.SetClassName(value); serializationConfig.SetGlobalSerializerConfig(globalSerializerConfig); } } } }
public XmlRpcResponseDeserializer(SerializerConfig config) : base(config) { }
public void ComplexPersonSerializing() { using (var config = new SerializerConfig()) { config.Match <ComplexPerson>(); using (var serializer = config.Create()) { IList <ComplexPerson> input = new List <ComplexPerson> { new ComplexPerson { Name = "Sarah", Age = 37, Children = new List <ComplexPerson> { new ComplexPerson { Name = "John", Age = 14, Children = new List <ComplexPerson>() } } }, new ComplexPerson { Name = "Julia", Age = 42, Children = new List <ComplexPerson> { new ComplexPerson { Name = "Sandra", Age = 20, Children = null } } }, new ComplexPerson { Name = "Sam", Age = 14, Children = null } }; using (var stream = CreateStream()) { serializer.Serialize(stream, input); stream.Seek(0); var result = serializer.Deserialize <IList <ComplexPerson> >(stream); Assert.Equal(3, result.Count); var sarah = result[0]; Assert.Equal("Sarah", sarah.Name); Assert.Equal(37, sarah.Age); Assert.Single(sarah.Children); var john = sarah.Children[0]; Assert.Equal("John", john.Name); Assert.Equal(14, john.Age); Assert.Empty(john.Children); var julia = result[1]; Assert.Equal("Julia", julia.Name); Assert.Equal(42, julia.Age); Assert.Single(julia.Children); var sandra = julia.Children[0]; Assert.Equal("Sandra", sandra.Name); Assert.Equal(20, sandra.Age); Assert.Null(sandra.Children); var sam = result[2]; Assert.Equal("Sam", sam.Name); Assert.Equal(14, sam.Age); Assert.Null(sam.Children); } } } }
private void StoreMessages(NetworkStream providedStream) { var config = new SerializerConfig(); config.Advanced.PersistTypeCache = true; config.OnResolveFormatter.Add((c, t) => { if (t == typeof(HashSet <byte[]>)) { return(new NetCore.NetCore_Extensions.HashSetFormatterThatKeepsItsComparer()); } return(null); // continue searching }); var serializer = new CerasSerializer(config); TcpListener server = null; Socket socket = null; NetworkStream networkStream = null; if (providedStream != null) { networkStream = providedStream; } try { if (networkStream == null) { server = new TcpListener((IP == "127.0.0.1" ? IPAddress.Loopback : IPAddress.Any), Port); server.Start(); socket = KillableAcceptSocket(server); networkStream = new NetworkStream(socket); server.Stop(); } networkStream.ReadTimeout = 20; networkStream.WriteTimeout = int.MaxValue; //Using {BOOP} commands routed through UDP/TCP if (spec.Side == NetworkSide.CLIENT) { SendMessage(new NetCoreAdvancedMessage("{HI}")); //The exchange of {HI} command confirms that link is established on Receiving } while (true) { if (networkStream != null && networkStream.DataAvailable) { if (spec.Side == NetworkSide.SERVER && (!socket?.Connected ?? true)) { return; } NetCoreAdvancedMessage message = null; try { using (MemoryStream ms = new MemoryStream()) { Stopwatch sw = new Stopwatch(); sw.Start(); //Read the size int lengthToReceive = 0; byte[] _lengthToReceive = new byte[4]; networkStream.Read(_lengthToReceive, 0, _lengthToReceive.Length); lengthToReceive = BitConverter.ToInt32(_lengthToReceive, 0); //Console.WriteLine("I want this many bytes: " + lengthToReceive); //Now read until we have that many bytes long bytesRead = CopyBytes(lengthToReceive, networkStream, ms); //Console.WriteLine("I got this many bytes: " + bytesRead); //Deserialize it ms.Position = 0; //cmd = (RTC_Command)binaryFormatter.Deserialize(ms); var temp = ms.ToArray(); message = serializer.Deserialize <NetCoreAdvancedMessage>(temp); sw.Stop(); if (message.Type != "{BOOP}" && sw.ElapsedMilliseconds > 50) { Console.WriteLine("It took " + sw.ElapsedMilliseconds + " ms to deserialize cmd " + message.Type + " of " + temp.Length + " bytes"); } } } catch { throw; } if (message != null) { if (message.Type == "{RETURNVALUE}") { spec.Connector.watch.AddReturn(message); } else { spec.Connector.hub.QueueMessage(message); } } } while (PeerMessageQueue.Count > 0) { NetCoreMessage pendingMessage; lock (PeerMessageQueueLock) { pendingMessage = PeerMessageQueue.First.Value; PeerMessageQueue.RemoveFirst(); } try { Stopwatch sw = new Stopwatch(); sw.Start(); //Write the length of the command to the first four bytes byte[] buf = serializer.Serialize(pendingMessage); //Write the length of the incoming object to the NetworkStream byte[] length = BitConverter.GetBytes(buf.Length); networkStream.Write(length, 0, length.Length); networkStream.Write(buf, 0, buf.Length); sw.Stop(); if (pendingMessage.Type != "{BOOP}" && sw.ElapsedMilliseconds > 50) { Console.WriteLine("It took " + sw.ElapsedMilliseconds + " ms to serialize backCmd " + pendingMessage.Type + " of " + buf.Length + " bytes"); } } catch { throw; } if (pendingMessage.Type == "{BYE}") { lock (PeerMessageQueueLock) //Since we're shutting down, let's clear the message queue PeerMessageQueue?.Clear(); } if (status == NetworkStatus.DISCONNECTED || status == NetworkStatus.CONNECTIONLOST) { //If the link's status changed from an outside factor, we want to stop the thread. lock (PeerMessageQueueLock) PeerMessageQueue?.Clear(); return; } } Thread.Sleep(spec.messageReadTimerDelay); } } catch (Exception ex) { if (ex is ThreadAbortException) { ConsoleEx.WriteLine("Ongoing TCPLink Thread Killed"); } else if (ex.InnerException != null && ex.InnerException is SocketException) { ConsoleEx.WriteLine("Ongoing TCPLink Socket Closed during use"); } else if (ex is SerializationException) { ConsoleEx.WriteLine("Ongoing TCPLink Closed during Serialization operation"); } else if (ex is ObjectDisposedException) { ConsoleEx.WriteLine("Ongoing TCPLink Closed during Socket acceptance"); } else { DiscardException(ex); } } finally { //Let's force close everything JUST IN CASE try { networkStream?.Close(); networkStream?.Dispose(); } catch { } //nobody cares why this failed try { socket?.Shutdown(SocketShutdown.Both); socket?.Dispose(); } catch { } //nobody cares why this failed try { server?.Stop(); } catch (Exception ex) { DiscardException(ex); } if (status == NetworkStatus.CONNECTED) { status = (expectingSomeone ? NetworkStatus.CONNECTIONLOST : NetworkStatus.DISCONNECTED); } else if (status != NetworkStatus.CONNECTIONLOST) { status = NetworkStatus.DISCONNECTED; } //Kill synced query if happenning spec.Connector.watch.Kill(); } }
static (CerasSerializer, List <Type>) CreateSerializerAndTargets(IEnumerable <Assembly> asms) { // Find config method and create a SerializerConfig SerializerConfig config = new SerializerConfig(); var configMethods = asms.SelectMany(a => a.GetTypes()) .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) .Where(m => m.GetCustomAttribute <CerasAutoGenConfigAttribute>() != null) .ToArray(); if (configMethods.Length > 1) { throw new Exception("Found more than one method with the CerasAutoGenConfig attribute!"); } if (configMethods.Length == 1) { config = (SerializerConfig)configMethods[0].Invoke(null, null); } config.VersionTolerance.Mode = VersionToleranceMode.Disabled; // ensure VersionTolerance is off so we don't accidentally get 'SchemaDynamicFormatter' var ceras = new CerasSerializer(config); // Start with KnownTypes... HashSet <Type> newTypes = new HashSet <Type>(); newTypes.AddRange(config.KnownTypes); // And also include all marked types var marker = typeof(CerasAutoGenFormatterAttribute); bool HasMarker(Type t) => t.GetCustomAttributes(true) .Any(a => a.GetType().FullName == marker.FullName); var markedTargets = asms.SelectMany(a => a.GetTypes()) .Where(t => !t.IsAbstract && HasMarker(t)); newTypes.AddRange(markedTargets); // Go through each type, add all the member-types it wants to serialize as well HashSet <Type> allTypes = new HashSet <Type>(); while (newTypes.Any()) { // Get first, remove from "to explore" list, and add it to the "done" list. var t = newTypes.First(); newTypes.Remove(t); allTypes.Add(t); if (CerasSerializer.IsPrimitiveType(t)) { // Skip int, string, Type, ... continue; } if (t.IsAbstract || t.ContainsGenericParameters) { // Can't explore abstract or open generics continue; } // Explore the type, add all member types var schema = ceras.GetTypeMetaData(t).PrimarySchema; foreach (var member in schema.Members) { if (!allTypes.Contains(member.MemberType)) { newTypes.Add(member.MemberType); } } } // Only leave things that use DynamicFormatter, or have the marker attribute List <Type> targets = new List <Type>(); foreach (var t in allTypes) { var f = ceras.GetSpecificFormatter(t); var fType = f.GetType(); if (fType.IsGenericType && fType.GetGenericTypeDefinition().Name == typeof(DynamicFormatter <int>).GetGenericTypeDefinition().Name) { targets.Add(t); } else if (HasMarker(t)) { targets.Add(t); } } return(ceras, targets); }
public XmlParser(SerializerConfig serializerConfig) { _config = serializerConfig; }
static void EnsureSealedTypesThrowsException() { // // 1. Check while serializing // var obj = new List <object>(); obj.Add(5); obj.Add(DateTime.Now); obj.Add("asdasdas"); obj.Add(new Person() { Name = "abc" }); var config = new SerializerConfig(); config.KnownTypes.Add(typeof(List <>)); config.KnownTypes.Add(typeof(int)); // Some types not added on purpose // Should be true by default! Debug.Assert(config.Advanced.SealTypesWhenUsingKnownTypes); var ceras = new CerasSerializer(config); try { ceras.Serialize(obj); Debug.Assert(false, "this line should not be reached, we want an exception here!"); } catch (Exception e) { // all good } // // 2. Check while deserializing // config = new SerializerConfig(); config.KnownTypes.Add(typeof(List <>)); config.KnownTypes.Add(typeof(int)); config.Advanced.SealTypesWhenUsingKnownTypes = false; ceras = new CerasSerializer(config); var data = ceras.Serialize(obj); config = new SerializerConfig(); config.KnownTypes.Add(typeof(List <>)); config.KnownTypes.Add(typeof(int)); config.Advanced.SealTypesWhenUsingKnownTypes = true; ceras = new CerasSerializer(config); try { ceras.Deserialize <List <object> >(data); Debug.Assert(false, "this line should not be reached, we want an exception here!"); } catch (Exception e) { // all good } }
static void ReadonlyTest() { // Test #1: // By default the setting is off. Fields are ignored. { SerializerConfig config = new SerializerConfig(); CerasSerializer ceras = new CerasSerializer(config); ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "xyz", new ReadonlyFieldsTest.ContainerThingA { Setting1 = 10, Setting2 = "asdasdas" }); var data = ceras.Serialize(obj); var cloneNew = ceras.Deserialize <ReadonlyFieldsTest>(data); Debug.Assert(cloneNew.Int == 1); Debug.Assert(cloneNew.String == "a"); Debug.Assert(cloneNew.Container == null); } // Test #2A: // In the 'Members' mode we expect an exception for readonly value-typed fields. { SerializerConfig config = new SerializerConfig(); config.Advanced.ReadonlyFieldHandling = ReadonlyFieldHandling.Members; config.ConfigType <ReadonlyFieldsTest>() .ConfigMember(f => f.Int).Include() .ConfigMember(f => f.String).Include(); CerasSerializer ceras = new CerasSerializer(config); ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "55555", new ReadonlyFieldsTest.ContainerThingA { Setting1 = 555555, Setting2 = "555555555" }); var data = ceras.Serialize(obj); ReadonlyFieldsTest existingTarget = new ReadonlyFieldsTest(6, "66666", null); bool gotException = false; try { var cloneNew = ceras.Deserialize <ReadonlyFieldsTest>(data); } catch (Exception ex) { gotException = true; } Debug.Assert(gotException); // We want an exception } // Test #2B: // In the 'Members' mode (when not dealing with value-types) // we want Ceras to re-use the already existing object { SerializerConfig config = new SerializerConfig(); config.Advanced.ReadonlyFieldHandling = ReadonlyFieldHandling.Members; config.ConfigType <ReadonlyFieldsTest>() .ConfigMember(f => f.Int).Exclude() .ConfigMember(f => f.String).Exclude() .ConfigMember(f => f.Container).Include(ReadonlyFieldHandling.Members); CerasSerializer ceras = new CerasSerializer(config); ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "55555", new ReadonlyFieldsTest.ContainerThingA { Setting1 = 555555, Setting2 = "555555555" }); var data = ceras.Serialize(obj); var newContainer = new ReadonlyFieldsTest.ContainerThingA { Setting1 = -1, Setting2 = "this should get overwritten" }; ReadonlyFieldsTest existingTarget = new ReadonlyFieldsTest(6, "66666", newContainer); // populate existing data ceras.Deserialize <ReadonlyFieldsTest>(ref existingTarget, data); // The simple fields should have been ignored Debug.Assert(existingTarget.Int == 6); Debug.Assert(existingTarget.String == "66666"); // The reference itself should not have changed Debug.Assert(existingTarget.Container == newContainer); // The content of the container should be changed now Debug.Assert(newContainer.Setting1 == 555555); Debug.Assert(newContainer.Setting2 == "555555555"); } // Test #3 // In 'ForcedOverwrite' mode Ceras should fix all possible mismatches by force (reflection), // which means that it should work exactly like as if the field were not readonly. { SerializerConfig config = new SerializerConfig(); config.Advanced.ReadonlyFieldHandling = ReadonlyFieldHandling.ForcedOverwrite; CerasSerializer ceras = new CerasSerializer(config); // This time we want Ceras to fix everything, reference mismatches and value mismatches alike. ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "55555", new ReadonlyFieldsTest.ContainerThingA { Setting1 = 324, Setting2 = "1134" }); var data = ceras.Serialize(obj); ReadonlyFieldsTest existingTarget = new ReadonlyFieldsTest(123, null, new ReadonlyFieldsTest.ContainerThingB()); // populate existing object ceras.Deserialize <ReadonlyFieldsTest>(ref existingTarget, data); // Now we really check for everything... // Sanity check, no way this could happen, but lets make sure. Debug.Assert(ReferenceEquals(obj, existingTarget) == false); // Fields should be like in the original Debug.Assert(existingTarget.Int == 5); Debug.Assert(existingTarget.String == "55555"); // The container type was wrong, Ceras should have fixed that by instantiating a different object // and writing that into the readonly field. var container = existingTarget.Container as ReadonlyFieldsTest.ContainerThingA; Debug.Assert(container != null); // Contents of the container should be correct as well Debug.Assert(container.Setting1 == 324); Debug.Assert(container.Setting2 == "1134"); } // Test #4: // Everything should work fine when using the MemberConfig attribute as well { var ceras = new CerasSerializer(); var obj = new ReadonlyFieldsTest2(); obj.Numbers.Clear(); obj.Numbers.Add(234); var data = ceras.Serialize(obj); var clone = new ReadonlyFieldsTest2(); var originalList = clone.Numbers; ceras.Deserialize(ref clone, data); Debug.Assert(originalList == clone.Numbers); // actual reference should not have changed Debug.Assert(clone.Numbers.Count == 1); // amount of entries should have changed Debug.Assert(clone.Numbers[0] == 234); // entry itself should be right } // todo: also test the case where the existing object does not match the expected type }
static void ExpressionTreesTest() { // Primitive test (private readonly in a base type) { SerializerConfig config = new SerializerConfig(); config.ConfigType <ReadonlyTestClass>() .ConstructByUninitialized() .SetReadonlyHandling(ReadonlyFieldHandling.ForcedOverwrite) .SetTargetMembers(TargetMember.PrivateFields); var ceras = new CerasSerializer(config); var obj = new ReadonlyTestClass("a"); var data = ceras.Serialize(obj); var clone = ceras.Deserialize <ReadonlyTestClass>(data); Debug.Assert(obj.GetName() == clone.GetName()); Debug.Assert(obj.GetBaseName() == clone.GetBaseName()); Console.WriteLine(); } // Small test 1 { Expression <Func <string, int, char> > getCharAtIndex = (text, index) => text.ElementAt(index); MethodCallExpression body = (MethodCallExpression)getCharAtIndex.Body; // Serialize and deserialize delegate SerializerConfig config = new SerializerConfig(); var ceras = new CerasSerializer(config); var data = ceras.Serialize <object>(body); var dataAsStr = Encoding.ASCII.GetString(data).Replace('\0', ' '); var clonedExp = (MethodCallExpression)ceras.Deserialize <object>(data); Debug.Assert(clonedExp.Method == body.Method); Debug.Assert(clonedExp.Arguments.Count == body.Arguments.Count); } // Small test 2 { // Test data string inputString = "abcdefgh"; Expression <Func <string, int, char> > getCharAtIndex = (text, index) => (text.ElementAt(index).ToString() + text[index])[0]; var del1 = getCharAtIndex.Compile(); char c1 = del1(inputString, 2); // Serialize and deserialize expression SerializerConfig config = new SerializerConfig(); var ceras = new CerasSerializer(config); var data = ceras.Serialize(getCharAtIndex); var dataAsStr = Encoding.ASCII.GetString(data).Replace('\0', ' '); var clonedExp = ceras.Deserialize <Expression <Func <string, int, char> > >(data); // Compile the restored expression, check if it works and returns the same result var del2 = clonedExp.Compile(); // Check single case var c2 = del2(inputString, 2); Debug.Assert(c1 == c2); // Check all cases for (int i = 0; i < inputString.Length; i++) { Debug.Assert(del1(inputString, i) == del2(inputString, i)); } } }
/* #if NETFRAMEWORK * static void TestDynamic() * { * dynamic dyn = new ExpandoObject(); * dyn.number = 5; * dyn.list = new List<string> { "a", "b"}; * dyn.c = "c"; * dyn.func = new Func<string>(((object)dyn).ToString); * * var ceras = new CerasSerializer(); * var data = ceras.Serialize(dyn); * var dyn2 = ceras.Deserialize<dynamic>(data); * } #endif */ static void TestBitmapFormatter() { var config = new SerializerConfig(); config.Advanced.BitmapMode = BitmapMode.SaveAsBmp; var ceras = new CerasSerializer(config); var home = Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%"); var downloads = Path.Combine(home, "Downloads"); var images = new Image[] { // todo: add test images Image.FromFile(Path.Combine(downloads, @".png")), }; for (int iteration = 0; iteration < 5; iteration++) { var imgData1 = ceras.Serialize(images); var clones = ceras.Deserialize <Image[]>(imgData1); for (var cloneIndex = 0; cloneIndex < clones.Length; cloneIndex++) { var c = clones[cloneIndex]; c.Dispose(); clones[cloneIndex] = null; } } byte[] sharedBuffer = new byte[100]; int offset = 0; foreach (var sourceImage in images) { offset += ceras.Serialize(sourceImage, ref sharedBuffer, offset); } offset += ceras.Serialize(images, ref sharedBuffer, offset); int writtenLength = offset; List <Image> clonedImages = new List <Image>(); offset = 0; for (var i = 0; i < images.Length; i++) { Image img = null; ceras.Deserialize(ref img, sharedBuffer, ref offset); clonedImages.Add(img); } Image[] imageArrayClone = null; ceras.Deserialize(ref imageArrayClone, sharedBuffer, ref offset); // Ensure all bytes consumed again Debug.Assert(offset == writtenLength); foreach (var img in clonedImages) { img.Dispose(); } foreach (var img in imageArrayClone) { img.Dispose(); } }
public UserModel(SerializerConfig serializerConfig, DeserializerConfig deserializerConfig) : base(serializerConfig, deserializerConfig, "users") { }
public void Step1_SimpleUsage() { // // 1.) Simple usage // aka. "I'm here for the cool features! I want to optimize for max-performance later" var person = new Person { Name = "riki", Health = 100 }; var serializer = new CerasSerializer(); var data = serializer.Serialize(person); data.VisualizePrint("Simple Person"); var clone1 = serializer.Deserialize <Person>(data); Console.WriteLine($"Clone: Name={clone1.Name}, Health={clone1.Health}"); // 2.) Types // You can also serialize as <object>. // In that case the type information will be included. // If a type is written it will only be written ONCE, so a List<Person> will not suddenly waste a // ton of space by continously writing the type-names var objectData = serializer.Serialize <object>(person); objectData.VisualizePrint("Person as <object>"); var objectClone = serializer.Deserialize <object>(objectData); // // 3.) Improvement: // Recycle the serialization buffer by keeping the reference to it around. // Optionally we can even let Ceras create (or resize) the buffer for us. byte[] buffer = null; int writtenBytes = serializer.Serialize(person, ref buffer); // Now we could send this over the network, for example: // socket.Send(buffer, writtenBytes, SocketFlags.None); var clone2 = serializer.Deserialize <Person>(buffer); // // 4.) // Deciding what gets serialized // There are multiple ways to configure what members to serialize // Ceras determines member inclusion in this order: // // - a. Using the result of "ShouldSerializeMember". // This method can always override everything else. // If it returns "NoOverride" or the method is not set // the search for a decision continues. // // - b. [Ignore] and [Include] attributes on individual members // // - c. [MemberConfig] attribute // // - d. "DefaultTargets" setting in the SerializerConfig // which defaults to 'TargetMember.PublicFields' // SerializerConfig config = new SerializerConfig(); config.DefaultTargets = TargetMember.PublicProperties | TargetMember.PrivateFields; config.ShouldSerializeMember = m => SerializationOverride.NoOverride; // // 5.) Circular references // Serializers commonly have trouble serializing circular references. // Ceras supports every possible object-graph, and there's literally // nothing to do or configure, it just works out of the box. // Lets make an example anyway... var personA = new Person { Name = "alice" }; var personB = new Person { Name = "bob" }; personA.BestFriend = personB; personB.BestFriend = personA; var dataWithCircularReferences = serializer.Serialize(personA); dataWithCircularReferences.VisualizePrint("Circular references data"); var cloneA = serializer.Deserialize <Person>(dataWithCircularReferences); if (cloneA.BestFriend.BestFriend.BestFriend.BestFriend.BestFriend.BestFriend == cloneA) { Console.WriteLine("Circular reference serialization working as intended!"); } else { throw new Exception("There was some problem!"); } }
public XmlRpcClient(HttpClient client) { Configuration = new SerializerConfig(); _client = client; }
public void Step7_GameDatabase() { /* * Scenario: * We have "MyMonster" and "MyAbility" for a game. * We want to be able to easily serialize the whole graph, but we also * want MyMonster and MyAbility instances to be saved in their own files! * * Lets first take a look at the classes we're working with: */ MyMonster monster = new MyMonster(); monster.Name = "Skeleton Mage"; monster.Health = 250; monster.Mana = 100; monster.Abilities.Add(new MyAbility { Name = "Fireball", ManaCost = 12, Cooldown = 0.5f, }); monster.Abilities.Add(new MyAbility { Name = "Ice Lance", ManaCost = 14, Cooldown = 6, }); // We want to save monsters and abilities in their their own files. // // Using other serializers this would be a terribly time-consuming task. // How would a classic solution for that look like? (without Ceras) // We would have to add attributes or maybe even write custom serializers so the "root objects" // can be when they are referenced in another object.. // Then we'd need a separate field maybe where we'd save a list of IDs or something.... // And then at load(deserialization)-time we would have to manually load that list, and resolve the // objects they stand for... // And all that for literally every "foreign key" (as it is called in database terms). :puke: ! // // Ceras offers a much better approach. // Just implement the 'IExternalRootObject' interface so Ceras can obtain an "ID" of your objects. // So whenever Ceras sees one of your objects implementing that interface, it will just write the ID of the object instead. // You can generate that ID however you want, most people would proably use some sort of counter. // // When loading/deserializing an object again Ceras will ask you for the external objects (giving you the ID). // SerializerConfig config = new SerializerConfig(); // 1. Create a config with a (pretty simple) custom external-object-resolver. var myGameObjectsResolver = new MyGameObjectsResolver(); config.ExternalObjectResolver = myGameObjectsResolver; // 2. Using KnownTypes is not neccesary at all, it just makes the serialized data a bit smaller. config.KnownTypes.Add(typeof(MyAbility)); config.KnownTypes.Add(typeof(MyMonster)); config.KnownTypes.Add(typeof(List <>)); // Ceras will call "OnExternalObject" (if you provide a function) when it encounters one of your IExternalRootObjects. // // So what would you use that for? // Pretty often when serializing one object, you probably also want to know about all the other IExternalRootObjects that // part of the "object graph" in some way (referenced by the original object) so you can save them as well. // // Maybe it would be a good idea to also include the last time an object has changed. // Like, you could have an OnPropertyChanged and whenever something changes you'd set something like a 'LastModified' date. // Then later you have the list of all the IExternalRootObjects and you can check LastModified to see if you have // to serialize and save it into a file again, or if the object is still up to date. // // In our example that means when serializing our Monster, the OnExternalObject function would // get called for the two abilities; that way we can save them as well. List <IExternalRootObject> externalObjects = new List <IExternalRootObject>(); config.OnExternalObject = obj => { externalObjects.Add(obj); }; var serializer = new CerasSerializer(config); myGameObjectsResolver.Serializer = serializer; var monsterData = serializer.Serialize(monster); // we can write this monster to the "monsters" sql-table now monsterData.VisualizePrint("Monster data"); MyGameDatabase.Monsters[monster.Id] = monsterData; // While serializing the monster we found some other external objects as well (the abilities) // Since we have collected them into a list we can serialize them as well. // Note: while in this example the abilities themselves don't reference any other external objects, // it is quite common in a real-world scenario that every object has tons of references, so keep in mind that // the following serializations would keep adding objects to our 'externalObjects' list. for (var i = 0; i < externalObjects.Count; i++) { var obj = externalObjects[i]; var abilityData = serializer.Serialize(obj); var id = obj.GetReferenceId(); MyGameDatabase.Abilities[id] = abilityData; abilityData.VisualizePrint($"Ability {id} data:"); } /* * Note: * * 1.) * Keep in mind that we can not share a deserialization buffer! * That means overwriting the buffer you passed to Deserialize while the deserialization is still in progress will cause problems. * "But why, when would I even attempt that??" * -> If you remember Step1 there's a part about re-using buffers. Well, in some cases you might be tempted to share a deserialization buffer as well. * For example you might think "if I use File.ReadAllBytes() for every object, that'd be wasteful, better use one big buffer and populate it from the file!" * The idea is nice and would work to avoid creating a large buffer each time you want to read an object; but when combining it with this IExternalObject idea, * things begin to break down because: * * Lets say you have a Monster1.bin file, and load it into the shared buffer. Now while deserializing Ceras realizes that the monster also has a reference to Spell3.bin. * It will send a request to your OnExternalObject function, asking for Type=Spell ID=3. * That's when you'd load the Spell3.bin data into the shared buffer, OVERWRITING THE DATA of the monster that is still being deserialized. * * In other words: Just make sure to not overwrite a buffer before the library is done with it (which should be common sense for any programmer tbh :P) * * 2.) * Consider a situation where we have 2 Person objects, both refering to each other (like the BestFriend example in Step1) * And now we'd like to load one person again. * Obviously Ceras has to also load the second person, so it will request it from you * Of course you again load the file (this time the requested person2.bin) and deserialize it. * Now! While deserializing person2 Ceras sees that it needs Person1! * And it calls your OnExternalObject again... * * > "Oh no, its an infinite loop, how to deal with this?" * * No problem. What you do is: * At the very start before deserializing, you first create an empty object: * var p = new Person(); * and then you add it to a dictionary! * myDictionary.Add(id, p); * * And then you call Ceras in "populate" mode, passing the object you created. * ceras.Deserialize(ref p, data); * * And you do it that way evertime something gets deserialized. * Now the problem is solved: While deserializing Person2 ceras calls your load function, and this time you already have an object! * Yes, it is not yet fully populated, but that doesn't matter at all. What matters is that the reference matches. * * * If this was confusing to you wait until I wrote another, even more detailed guide or something (or just open an issue on github!) * * * (todo: write better guide; maybe even write some kind of "helper" class that deals with all of this maybe?) */ // Load the data again: var loadedMonster = serializer.Deserialize <MyMonster>(MyGameDatabase.Monsters[1]); var ability1 = serializer.Deserialize <MyAbility>(MyGameDatabase.Abilities[1]); var ability2 = serializer.Deserialize <MyAbility>(MyGameDatabase.Abilities[2]); }
public void Step1_SimpleUsage() { // // 1.) Simple usage // aka. "I'm here for the cool features! I want to optimize for max-performance later" var person = new Person { Name = "riki", Health = 100 }; var ceras = new CerasSerializer(); var data = ceras.Serialize(person); data.VisualizePrint("Simple Person"); var clone1 = ceras.Deserialize <Person>(data); Console.WriteLine($"Clone: Name={clone1.Name}, Health={clone1.Health}"); // 2.) Types // You can also serialize as <object>. // In that case the type information will be included. // If a type is written it will only be written ONCE, so a List<Person> will not suddenly waste a // ton of space by continously writing the type-names var objectData = ceras.Serialize <object>(person); objectData.VisualizePrint("Person as <object>"); var objectClone = ceras.Deserialize <object>(objectData); // // 3.) Improvement: // Recycle the serialization buffer by keeping the reference to it around. // Optionally we can even let Ceras create (or resize) the buffer for us. byte[] buffer = null; int writtenBytes = ceras.Serialize(person, ref buffer); // Now we could send this over the network, for example: // socket.Send(buffer, writtenBytes, SocketFlags.None); var clone2 = ceras.Deserialize <Person>(buffer); // // 4.) // Deciding what gets serialized // There are multiple ways to configure what members to serialize // Ceras determines member inclusion in this order: // // - a. Any custom configuration using ConfigType<T> or ConfigType(type) // // - b. [Include] and [Exclude] attributes on individual members // // - c. [MemberConfig] attribute // // - d. "DefaultTargets" setting in the SerializerConfig // which defaults to 'TargetMember.PublicFields' // SerializerConfig config = new SerializerConfig(); config.DefaultTargets = TargetMember.PublicProperties | TargetMember.PrivateFields; config.ConfigType <Person>() .ConfigMember(p => p.Name).Include() .ConfigMember(p => p.BestFriend).Include(); // // 5.) Circular references // Serializers commonly have trouble serializing circular references. // Ceras supports every possible object-graph, and there's literally // nothing to do or configure, it just works out of the box. // Lets make an example anyway... var personA = new Person { Name = "alice" }; var personB = new Person { Name = "bob" }; personA.BestFriend = personB; personB.BestFriend = personA; var dataWithCircularReferences = ceras.Serialize(personA); dataWithCircularReferences.VisualizePrint("Circular references data"); var cloneA = ceras.Deserialize <Person>(dataWithCircularReferences); if (cloneA.BestFriend.BestFriend.BestFriend.BestFriend.BestFriend.BestFriend == cloneA) { Console.WriteLine("Circular reference serialization working as intended!"); } else { throw new Exception("There was some problem!"); } // Works with self-references! // Ceras maintains object references while deserializing, even if the object a field/prop points to doesn't exist yet var personC = new Person { Name = "abc" }; personC.BestFriend = personC; var cloneC = ceras.Deserialize <Person>(ceras.Serialize(personC)); Debug.Assert(cloneC.BestFriend.BestFriend.BestFriend.BestFriend == cloneC); // Fully maintains identity of objects! // There is only one actual object instance here (personC). The array refers to the same object two times. // While Ceras deserializes the array it only creates a single instance of 'Person', exactly as intended. Person[] personArray = new Person[2]; personArray[0] = personC; personArray[1] = personC; var personArrayClone = ceras.Deserialize <Person[]>(ceras.Serialize(personArray)); Debug.Assert(personArray[0] == personArray[1]); Debug.Assert(ReferenceEquals(personArray[0], personArray[1])); Debug.Assert(personArray[0].BestFriend == personArray[1].BestFriend); Debug.Assert(personArray[0].BestFriend.BestFriend == personArray[1]); }
public void Setup() { // // Create example data var parent1 = new Person { Age = -901, FirstName = "Parent 1", LastName = "abc", Sex = Sex.Male, }; var parent2 = new Person { Age = 7881964, FirstName = "Parent 2", LastName = "xyz", Sex = Sex.Female, }; _person = new Person { Age = 5, FirstName = "Riki", LastName = "Example Person Object", Sex = Sex.Unknown, Parent1 = parent1, Parent2 = parent2, }; _list = Enumerable.Range(25000, 100).Select(x => new Person { Age = x, FirstName = "a", LastName = "b", Sex = Sex.Female }).ToArray(); // // Config Serializers _wire = new Wire.Serializer(new Wire.SerializerOptions(knownTypes: new Type[] { typeof(Person), typeof(Person[]) })); _netSerializer = new NetSerializer.Serializer(rootTypes: new Type[] { typeof(Person), typeof(Person[]) }); var config = new SerializerConfig(); config.DefaultTargets = TargetMember.AllPublic; var knownTypes = new[] { typeof(Person), typeof(List <>), typeof(Person[]) }; config.KnownTypes.AddRange(knownTypes); config.PreserveReferences = false; _ceras = new CerasSerializer(config); // // Run each serializer once to verify they work correctly! if (!Equals(RunCeras(_person), _person)) { ThrowError(); } if (!Equals(RunJson(_person), _person)) { ThrowError(); } if (!Equals(RunMessagePackCSharp(_person), _person)) { ThrowError(); } if (!Equals(RunProtobuf(_person), _person)) { ThrowError(); } if (!Equals(RunWire(_person), _person)) { ThrowError(); } if (!Equals(RunNetSerializer(_person), _person)) { ThrowError(); } void ThrowError() => throw new InvalidOperationException("Cannot continue with the benchmark because a serializer does not round-trip an object correctly. (Benchmark results will be wrong)"); }
public void BlittingEnums() { // Usually we don't use the reinterpret-formatter for enum members so we can minimize the size (varint encoding) var stressTestValues = new[] { long.MinValue, long.MaxValue, -1, 0, 1, 5, 1000, 255, 256, rngByte, rngByte, rngByte, rngLong, rngLong, rngLong, rngLong, rngLong, rngLong, }; var config = new SerializerConfig(); config.OnConfigNewType = t => t.CustomResolver = (c, t2) => c.Advanced.GetFormatterResolver <ReinterpretFormatterResolver>().GetFormatter(t2); //config.OnConfigNewType = t => t.CustomFormatter = (IFormatter)Activator.CreateInstance(typeof(EnumFormatterUnsafe<>).MakeGenericType(t.Type)); var ceras = new CerasSerializer(config); var typesToTest = new[] { typeof(TestEnumInt8), typeof(TestEnumUInt8), typeof(TestEnumInt16), typeof(TestEnumUInt16), typeof(TestEnumInt64), typeof(TestEnumUInt64), }; var serializeMethod = typeof(CerasSerializer).GetMethods().First(m => m.Name == nameof(CerasSerializer.Serialize) && m.GetParameters().Length == 1); var deserializeMethod = typeof(CerasSerializer).GetMethods().First(m => m.Name == nameof(CerasSerializer.Deserialize) && m.GetParameters().Length == 1); foreach (var t in typesToTest) { Type baseType = t.GetEnumUnderlyingType(); int expectedSize = Marshal.SizeOf(baseType); var values = Enum.GetValues(t).Cast <object>().Concat(stressTestValues.Cast <object>()); foreach (var v in values) { var obj = Enum.ToObject(t, v); // We must call Serialize<T>, and we can't use <object> because that would embed the type information var data = (byte[])serializeMethod.MakeGenericMethod(t).Invoke(ceras, new object[] { obj }); Assert.True(data.Length == expectedSize); var cloneObj = deserializeMethod.MakeGenericMethod(t).Invoke(ceras, new object[] { data }); Assert.True(obj.Equals(cloneObj)); } } Assert.True(ceras.Serialize(TestEnumInt8.a).Length == 1); Assert.True(ceras.Serialize(TestEnumUInt8.a).Length == 1); Assert.True(ceras.Serialize(TestEnumInt16.a).Length == 2); Assert.True(ceras.Serialize(TestEnumUInt16.a).Length == 2); Assert.True(ceras.Serialize(TestEnumInt64.a).Length == 8); Assert.True(ceras.Serialize(TestEnumUInt64.a).Length == 8); }
static (CerasSerializer, List <Type>, Dictionary <Type, Type>) CreateSerializerAndTargets(IEnumerable <Assembly> asms) { // Find config method and create a SerializerConfig SerializerConfig config = new SerializerConfig(); var configMethods = asms.SelectMany(a => a.GetTypes()) .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) .Where(m => m.GetCustomAttribute <AotSerializerConfigAttribute>() != null) .ToArray(); if (configMethods.Length > 1) { throw new Exception("Found more than one method with the CerasAutoGenConfig attribute!"); } if (configMethods.Length == 1) { config = (SerializerConfig)configMethods[0].Invoke(null, null); } var ceras = new CerasSerializer(config); // Start with KnownTypes and user-marked types... HashSet <Type> newTypes = new HashSet <Type>(); newTypes.AddRange(config.KnownTypes); newTypes.AddRange(asms.SelectMany(a => a.GetTypes()).Where(t => !t.IsAbstract && IsMarkedForAot(t))); // Go through each type, add all the member-types it wants to serialize as well HashSet <Type> processedTypes = new HashSet <Type>(); Dictionary <Type, Type> aotHint = new Dictionary <Type, Type>(); while (newTypes.Any()) { // Get first, remove from "to explore" list, and add it to the "done" list. var t = newTypes.First(); if (t.IsArray) { t = t.GetElementType(); } newTypes.Remove(t); processedTypes.Add(t); if (CerasSerializer.IsPrimitiveType(t)) { // Skip int, string, Type, ... continue; } if (t.IsAbstract || t.ContainsGenericParameters) { // Can't explore abstract or open generics continue; } // Explore the type, add all member types var schema = ceras.GetTypeMetaData(t).PrimarySchema; foreach (var member in schema.Members) { if (!processedTypes.Contains(member.MemberType)) { newTypes.Add(member.MemberType); } } var formatter = ceras.GetSpecificFormatter(t); var formatterType = formatter.GetType(); var needAot = CerasHelpers.IsDynamicFormatter(formatterType) || CerasHelpers.IsSchemaDynamicFormatter(formatterType) || IsMarkedForAot(t); if (needAot) { continue; } aotHint[t] = formatterType; foreach (var type in formatterType.GenericTypeArguments) { if (!processedTypes.Contains(type)) { newTypes.Add(type); } } } // Only leave things that use DynamicFormatter, or have the marker attribute List <Type> targets = new List <Type>(); foreach (var t in processedTypes) { if (CerasSerializer.IsPrimitiveType(t)) { continue; // Skip int, string, Type, ... } if (t.IsAbstract || t.ContainsGenericParameters) { continue; // Abstract or open generics can't have instances... } if (aotHint.ContainsKey(t)) { continue; } targets.Add(t); } return(ceras, targets, aotHint); bool IsMarkedForAot(Type t) { if (t.GetCustomAttributes(true).Any(a => a.GetType().FullName == Marker.FullName)) { return(true); // has 'Generate Formatter' attribute } return(false); } }
static void Main(string[] args) { #if NET45 global::System.Console.WriteLine("Running on NET4.5"); #elif NET451 global::System.Console.WriteLine("Running on NET4.5.1"); #elif NET452 global::System.Console.WriteLine("Running on NET4.5.2"); #elif NET47 global::System.Console.WriteLine("Running on NET4.7"); #elif NET47 global::System.Console.WriteLine("Running on NET4.7"); #elif NET471 global::System.Console.WriteLine("Running on NET4.7.1"); #elif NET472 global::System.Console.WriteLine("Running on NET4.7.2"); #elif NETSTANDARD2_0 global::System.Console.WriteLine("Running on NET STANDARD 2.0"); #else #error Unhandled framework version! #endif new Internals().FastCopy(); new BuiltInTypes().Bitmap(); var config = new SerializerConfig(); config.Advanced.BitmapMode = BitmapMode.SaveAsBmp; var ceras = new CerasSerializer(config); var home = System.Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%"); var downloads = Path.Combine(home, "Downloads"); var images = new Image[] { Image.FromFile(Path.Combine(downloads, @"68747470733a2f2f692e696d6775722e636f6d2f513839365567562e706e67.png")), Image.FromFile(Path.Combine(downloads, @"7plX.gif")), Image.FromFile(Path.Combine(downloads, @"TexturesCom_BrickOldMixedSize0012_1_seamless_S.jpg")), Image.FromFile(Path.Combine(downloads, @"New Drawing.png")), Image.FromFile(Path.Combine(downloads, @"smoke_1_40_128_corrected.png")), Image.FromFile(Path.Combine(downloads, @"Spheres_thumb9.png")), }; for (int iteration = 0; iteration < 5; iteration++) { var imgData1 = ceras.Serialize(images); var clones = ceras.Deserialize <Image[]>(imgData1); for (var cloneIndex = 0; cloneIndex < clones.Length; cloneIndex++) { var c = clones[cloneIndex]; c.Dispose(); clones[cloneIndex] = null; } } byte[] sharedBuffer = new byte[100]; int offset = 0; foreach (var sourceImage in images) { offset += ceras.Serialize(sourceImage, ref sharedBuffer, offset); } offset += ceras.Serialize(images, ref sharedBuffer, offset); int writtenLength = offset; List <Image> clonedImages = new List <Image>(); offset = 0; for (var i = 0; i < images.Length; i++) { Image img = null; ceras.Deserialize(ref img, sharedBuffer, ref offset); clonedImages.Add(img); } Image[] imageArrayClone = null; ceras.Deserialize(ref imageArrayClone, sharedBuffer, ref offset); // Ensure all bytes consumed again Debug.Assert(offset == writtenLength); foreach (var img in clonedImages) { img.Dispose(); } foreach (var img in imageArrayClone) { img.Dispose(); } }
public void TestWriteObjectWithCustomSerializable() { var config = new SerializationConfig(); var sc = new SerializerConfig() .SetImplementation(new CustomSerializer()) .SetTypeClass(typeof (CustomSerializableType)); config.AddSerializerConfig(sc); var serializationService = new SerializationServiceBuilder().SetPortableVersion(1) .AddPortableFactory(TestSerializationConstants.PORTABLE_FACTORY_ID, new TestPortableFactory()) .SetConfig(config).Build(); var foo = new CustomSerializableType {Value = "foo"}; var objectCarryingPortable1 = new ObjectCarryingPortable(foo); var data = serializationService.ToData(objectCarryingPortable1); var objectCarryingPortable2 = serializationService.ToObject<ObjectCarryingPortable>(data); Assert.AreEqual(objectCarryingPortable1, objectCarryingPortable2); }
static void Main(string[] args) { if (args.Length < 2) { var error = "Not enough arguments. The last argument is always the .cs file output path, all arguments before that are the input assemblies (.dll files) of your unity project. Example: \"C:\\MyUnityProject\\Temp\\bin\\Debug\\Assembly-CSharp.dll C:\\MyUnityProject\\Assets\\Scripts\\GeneratedFormatters.cs\""; Console.WriteLine(error); throw new ArgumentException(error); } inputAssemblies = args.Reverse().Skip(1).Reverse().ToArray(); outputCsFileName = args.Reverse().First(); var marker = typeof(CerasAutoGenFormatterAttribute); AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; var asms = inputAssemblies.Select(Assembly.LoadFrom); var targets = asms.SelectMany(a => a.GetTypes()) .Where(t => t.GetCustomAttributes(true) .Any(a => a.GetType().FullName == marker.FullName)) .Where(t => !t.IsAbstract) .ToList(); Console.WriteLine($"Found: {targets.Count} targets"); // Find config method and create a SerializerConfig SerializerConfig config = new SerializerConfig(); var configMethods = asms.SelectMany(a => a.GetTypes()) .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) .Where(m => m.GetCustomAttribute <CerasAutoGenConfigAttribute>() != null) .ToArray(); if (configMethods.Length > 1) { throw new Exception("Found more than one config method!"); } if (configMethods.Length == 1) { config = (SerializerConfig)configMethods[0].Invoke(null, null); } targets.AddRange(config.KnownTypes); var ceras = new CerasSerializer(config); StringBuilder fullCode = new StringBuilder(25 * 1000); fullCode.AppendLine("using Ceras;"); fullCode.AppendLine("using Ceras.Formatters;"); fullCode.AppendLine("namespace Ceras.GeneratedFormatters"); fullCode.AppendLine("{"); var setCustomFormatters = targets.Select(t => $"config.ConfigType<{t.FullName}>().CustomFormatter = new {t.Name}Formatter();"); fullCode.AppendLine($@" static class GeneratedFormatters {{ internal static void UseFormatters(SerializerConfig config) {{ {string.Join("\n", setCustomFormatters)} }} }} "); foreach (var t in targets) { SourceFormatterGenerator.Generate(t, ceras, fullCode); } fullCode.AppendLine("}"); Console.WriteLine($"Parsing..."); var syntaxTree = CSharpSyntaxTree.ParseText(fullCode.ToString()); Console.WriteLine($"Formatting..."); var workspace = new AdhocWorkspace(); var options = workspace.Options .WithChangedOption(CSharpFormattingOptions.IndentBlock, true) .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInAccessors, true) .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInControlBlocks, true) .WithChangedOption(CSharpFormattingOptions.NewLinesForBracesInTypes, true) .WithChangedOption(CSharpFormattingOptions.IndentBraces, false); syntaxTree = Formatter.Format(syntaxTree.GetRoot(), workspace, options).SyntaxTree; Console.WriteLine($"Saving..."); using (var fs = File.OpenWrite(outputCsFileName)) using (var w = new StreamWriter(fs)) { fs.SetLength(0); w.WriteLine(syntaxTree.ToString()); } // todo: maybe we'll generate an assembly instead of source code at some pointlater... //GenerateFormattersAssembly(targets); Thread.Sleep(300); Console.WriteLine($"> Done!"); }
public virtual SerializationConfig AddSerializerConfig(SerializerConfig serializerConfig) { GetSerializerConfigs().Add(serializerConfig); return this; }
public static void ToCERAS <T>(this T obj, string filename, SerializerConfig config) { byte[] final; final = ToCERAS(obj, config); File.WriteAllBytes(filename, final); }