diff --git a/README.md b/README.md
index c3f0f3b..b8ec125 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ in more complex cases, you still won't be implementing `Serializer`/`Deserialize
 ```ts
 import { registerSerialize, registerDeserialize } from 'serde'
 import { ISerializer } from 'serde/ser'
-import { IDeserializer, IIterableAccess } from 'serde/de'
+import { forward, IDeserializer, IIterableAccess, IMapAccess } from 'serde/de'
 import { fromString, toString } from 'serde-json-ts'
 
 class Vector2 {
@@ -113,30 +113,74 @@ class Vector2 {
     this.x = x
     this.y = y
   }
+
+  static serialize(serializer: ISerializer<string>, value: Vector2) {
+    const iter = serializer.serializeIterable() // returns an ISerializeIterable<void>
+    iter.serializeElement(value.x)
+    iter.serializeElement(value.y)
+    return iter.end()
+  }
+
+  static deserialize(deserializer: IDeserializer) {
+    return deserializer.deserializeIterable({
+      // we could implement visitNumber here, but we'll let the default
+      // deserializer handle it
+      visitIterable(access: IIterableAccess) {
+        const elements = []
+
+        for (const item of access) {
+          elements.push(item as number)
+        }
+
+        return new Vector2(elements[0], elements[1])
+      }
+    })
+  }
+}
+
+class Entity {
+  name: string
+  position: Vector2
+
+  constructor(name: string, position: Vector2) {
+    this.name = name
+    this.position = position
+  }
+
+  static serialize(serializer: ISerializer<string>, value: Entity) {
+    const ser = serializer.serializeObject()
+    ser.serializeEntry('name', value.name)
+    ser.serializeEntry('position', value.position)
+    return ser.end()
+  }
+
+  static deserialize(deserializer: IDeserializer) {
+    return deserializer.deserializeObject({
+      visitObject(access: IMapAccess) {
+        let name, position
+        for (const [key, value] of access) {
+          switch (key) {
+            case 'name':
+              name = value
+              break
+            case 'position':
+              // forward the deserialization to Vector2
+              position = forward(value as string, Vector2)
+              break
+          }
+        }
+
+        return new Entity(name as string, position as Vector2)
+      }
+    })
+  }
 }
 
 // we're registering to the global serde registry
-registerSerialize(Vector2, (serializer: ISerializer<void>, value: Vector2) => {
-  const iter = serializer.serializeIterable() // returns an ISerializeIterable<void>
-  iter.serializeElement(value.x)
-  iter.serializeElement(value.y)
-  return iter.end()
-})
-
-registerDeserialize(Vector2, (deserializer: IDeserializer) => deserializer.deserializeIterable({
-  // we could implement visitNumber here, but we'll let the default
-  // deserializer handle it
-  visitIterable(access: IIterableAccess) {
-    const elements = []
-
-    for (const item of access) {
-      elements.push(item as number)
-    }
-
-    return new Vector2(elements[0], elements[1])
-  }
-})
-)
+registerSerialize(Vector2, Vector2.serialize)
+registerDeserialize(Vector2, Vector2.deserialize)
+registerSerialize(Entity, Entity.serialize)
+registerDeserialize(Entity, Entity.deserialize)
 
 const one = new Vector2(1, 1)
 const serialized = toString(one)
@@ -146,5 +190,14 @@ console.log(serialized)
 const deserializedOne = fromString(serialized, Vector2)
 console.log(deserializedOne)
 // Vector2 { x: 1, y: 1 }
+
+const player = new Entity('Player', one)
+const serializedPlayer = toString(player)
+console.log(serializedPlayer)
+// {"name":"Player","position":[1,1]}
+
+const deserializedPlayer = fromString(serializedPlayer, Entity)
+console.log(deserializedPlayer)
+// Entity { name: 'Player', position: Vector2 { x: 1, y: 1 } }
 ```