changeset 49:beb4afce2f3e

Fixed (de)serializing through base class reference, including unit test.
author Jacob Carlborg <doob@me.com>
date Sat, 13 Aug 2011 15:57:19 +0200
parents a7dea44fa9e3
children 715cd0264c15
files orange/serialization/Serializer.d tests/BaseClass.d unittest.sh
diffstat 3 files changed, 132 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/orange/serialization/Serializer.d	Sat Aug 13 15:24:52 2011 +0200
+++ b/orange/serialization/Serializer.d	Sat Aug 13 15:57:19 2011 +0200
@@ -56,13 +56,15 @@
 			Id id;
 			string key;
 		}
-		
+
 		ErrorCallback errorCallback_;		
 		Archive archive_;
 		
 		size_t keyCounter;
 		Id idCounter;
 		
+		void delegate (Object, Mode mode) [ClassInfo] registeredTypes;
+		
 		RegisterBase[string] serializers;
 		RegisterBase[string] deserializers;
 		
@@ -95,6 +97,24 @@
 		setThrowOnErrorCallback();
 	}
 	
+	void register (T : Object) ()
+	{
+		registeredTypes[T.classinfo] = &downcastSerialize!(T);
+	}
+	
+	private void downcastSerialize (T : Object) (Object value, Mode mode)
+	{
+		auto casted = cast(T) value;
+		assert(casted);
+		assert(casted.classinfo is T.classinfo);
+		
+		if (mode == serializing)
+			objectStructSerializeHelper(casted);
+		
+		else
+			objectStructDeserializeHelper(casted);
+	}
+	
 	Archive archive ()
 	{
 		return archive_;
@@ -126,6 +146,7 @@
 		
 		serializers = null;
 		deserializers = null;
+		registeredTypes = null;
 		
 		serializedReferences = null;
 		deserializedReferences = null;
@@ -134,7 +155,10 @@
 		deserializedSlices = null;
 		
 		serializedValues = null;
+		deserializedValues = null;
+
 		serializedPointers = null;
+		deserializedPointers = null;
 		
 		hasBegunSerializing = false;
 		hasBegunDeserializing = false;
@@ -231,9 +255,20 @@
 				else
 				{				
 					if (isBaseClass(value))
-						throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a serializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__);
-
-					objectStructSerializeHelper(value);
+					{
+						if (auto serializer = value.classinfo in registeredTypes)
+							(*serializer)(value, serializing);
+						
+						else
+							throw new SerializationException(
+								`The object of the static type "` ~ T.stringof ~
+								`" have a different runtime type (` ~ runtimeType ~
+								`) and therefore needs to either register its type or register a serializer for its type "`
+								~ runtimeType ~ `".`, __FILE__, __LINE__);
+					}
+					
+					else
+						objectStructSerializeHelper(value);
 				}
 			});
 		});
@@ -469,9 +504,20 @@
 				else
 				{
 					if (isBaseClass(value))
-						throw new SerializationException(`The object of the static type "` ~ T.stringof ~ `" have a different runtime type (` ~ runtimeType ~ `) and therefore needs to register a deserializer for its type "` ~ runtimeType ~ `".`, __FILE__, __LINE__);
+					{
+						if (auto deserializer = value.classinfo in registeredTypes)
+							(*deserializer)(value, deserializing);
 
-					objectStructDeserializeHelper(value);					
+						else
+							throw new SerializationException(
+								`The object of the static type "` ~ T.stringof ~
+								`" have a different runtime type (` ~ runtimeType ~
+								`) and therefore needs to either register its type or register a deserializer for its type "`
+								~ runtimeType ~ `".`, __FILE__, __LINE__);
+					}
+					
+					else
+						objectStructDeserializeHelper(value);					
 				}
 			});
 		});
@@ -995,10 +1041,7 @@
 	
 	private bool isBaseClass (T) (T value)
 	{
-		auto name = value.classinfo.name;		
-		auto index = name.lastIndexOf('.');
-		
-		return T.stringof != name[index + 1 .. $];
+		return value.classinfo !is T.classinfo;
 	}
 	
 	private Id nextId ()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/BaseClass.d	Sat Aug 13 15:57:19 2011 +0200
@@ -0,0 +1,78 @@
+/**
+ * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
+ * Authors: Jacob Carlborg
+ * Version: Initial created: Aug 7, 2011
+ * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
+ */
+module tests.BaseClass;
+
+import orange.core.string;
+import orange.serialization.Serializer;
+import orange.serialization.archives.XMLArchive;
+import orange.test.UnitTester;
+import tests.Util;
+
+Serializer serializer;
+XMLArchive!(char) archive;
+
+class Base
+{
+	int a;
+	
+	int getA ()
+	{
+		return a;
+	}
+	
+	int getB ()
+	{
+		return a;
+	}
+}
+
+class Sub : Base
+{
+	int b;
+
+	int getB ()
+	{
+		return b;
+	}
+}
+
+Sub sub;
+Base base;
+
+unittest
+{
+	archive = new XMLArchive!(char);
+	serializer = new Serializer(archive);
+
+	sub = new Sub;
+	sub.a = 3;
+	sub.b = 4;
+	base = sub;
+
+	describe("serialize subclass through a base class reference") in {
+		it("should return serialized subclass with the static type \"Base\" and the runtime type \"tests.BaseClass.Sub\"") in {
+			serializer.reset;
+			serializer.register!(Sub);
+			serializer.serialize(base);
+	
+			assert(archive.data().containsDefaultXmlContent());
+			assert(archive.data().containsXmlTag("object", `runtimeType="tests.BaseClass.Sub" type="Base" key="0" id="0"`));
+			assert(archive.data().containsXmlTag("int", `key="b" id="1"`, "4"));
+			assert(archive.data().containsXmlTag("base", `type="Base" key="1" id="2"`));
+			assert(archive.data().containsXmlTag("int", `key="a" id="3"`, "3"));
+		};
+	};
+	
+	describe("deserialize subclass through a base class reference") in {
+		it("should return a deserialized subclass with the static type \"Base\" and the runtime type \"tests.BaseClass.Sub\"") in {
+			auto subDeserialized = serializer.deserialize!(Base)(archive.untypedData);
+
+			assert(sub.a == subDeserialized.getA);
+			assert(sub.b == subDeserialized.getB);
+		};
+	};
+}
\ No newline at end of file
--- a/unittest.sh	Sat Aug 13 15:24:52 2011 +0200
+++ b/unittest.sh	Sat Aug 13 15:57:19 2011 +0200
@@ -29,6 +29,7 @@
 tests/Array.d \
 tests/AssociativeArray.d \
 tests/AssociativeArrayReference.d \
+tests/BaseClass.d \
 tests/Enum.d \
 tests/Event.d \
 tests/Object.d \