diff --git a/colander/__init__.py b/colander/__init__.py
index 4d88226..128d179 100644
--- a/colander/__init__.py
+++ b/colander/__init__.py
@@ -416,6 +416,8 @@ class Mapping(SchemaType):
         for num, subnode in enumerate(node.children):
             name = subnode.name
             subval = value.pop(name, null)
+            if subval is null and subnode.optional:
+                continue
             try:
                 result[name] = callback(subnode, subval)
             except Invalid, e:
@@ -1249,6 +1251,7 @@ class SchemaNode(object):
     def __init__(self, typ, *children, **kw):
         self.typ = typ
         self.validator = kw.pop('validator', None)
+        self.optional = kw.pop('optional', False)
         self.default = kw.pop('default', null)
         self.missing = kw.pop('missing', _marker)
         self.name = kw.pop('name', '')
@@ -1269,7 +1272,7 @@ class SchemaNode(object):
         implies that a ``missing`` value was specified for this node."""
         if isinstance(self.missing, deferred):  # unbound schema with deferreds
             return True
-        return self.missing is _marker
+        return not self.optional and self.missing is _marker
 
     def serialize(self, appstruct=null):
         """ Serialize the :term:`appstruct` to a :term:`cstruct` based
@@ -1324,13 +1327,17 @@ class SchemaNode(object):
         defaults to :attr:`colander.null`.
         """
         if cstruct is null:
-            appstruct = self.missing
-            if appstruct is _marker:
-                raise Invalid(self, _('Required'))
-            if isinstance(appstruct, deferred): # unbound schema with deferreds
-                raise Invalid(self, _('Required'))
-            # We never deserialize or validate the missing value
-            return appstruct
+            if not self.optional:
+                appstruct = self.missing
+                if appstruct is _marker:
+                    raise Invalid(self, _('Required'))
+                if isinstance(appstruct, deferred): # unbound schema with
+                                                    # deferreds
+                    raise Invalid(self, _('Required'))
+                # We never deserialize or validate the missing value
+                return appstruct
+            else:
+                return self.missing
 
         appstruct = self.typ.deserialize(self, cstruct)
         if self.validator is not None:
diff --git a/colander/tests.py b/colander/tests.py
index 070c0ac..ffc7d71 100644
--- a/colander/tests.py
+++ b/colander/tests.py
@@ -463,6 +463,60 @@ class TestMapping(unittest.TestCase):
         self.assertEqual(result,
                          {'node': {'a': 1, 'b': 2}, 'node.appstruct': 2})
 
+    def test_deserialize_optional_subnode_value_not_exists(self):
+        from colander import MappingSchema
+        from colander import SchemaNode
+        from colander import Integer
+        class Schema(MappingSchema):
+            optional = SchemaNode(Integer())
+        schema = Schema()
+        result = schema.deserialize({'optional': 1})
+        self.assertEqual(result, {'optional': 1})
+
+    def test_deserialize_optional_subnode_value_exists(self):
+        from colander import MappingSchema
+        from colander import SchemaNode
+        from colander import Integer
+        class Schema(MappingSchema):
+            optional = SchemaNode(Integer(), optional=True)
+        schema = Schema()
+        result = schema.deserialize({})
+        self.assertEqual(result, {})
+
+    def test_deserialize_optional_subnode_w_validator_value_exists_error(self):
+        from colander import MappingSchema
+        from colander import SchemaNode
+        from colander import Integer
+        from colander import Range
+        from colander import Invalid
+        class Schema(MappingSchema):
+            optional = SchemaNode(Integer(), validator=Range(1), optional=True)
+        schema = Schema()
+        self.assertRaises(Invalid, schema.deserialize, {'optional': -1})
+
+    def test_deserialize_optional_subnode_w_validator_value_exists_ok(self):
+        from colander import MappingSchema
+        from colander import SchemaNode
+        from colander import Integer
+        from colander import Range
+        from colander import Invalid
+        class Schema(MappingSchema):
+            optional = SchemaNode(Integer(), validator=Range(1), optional=True)
+        schema = Schema()
+        result = schema.deserialize({'optional': 1})
+        self.assertEqual(result, {'optional': 1})
+
+    def test_deserialize_optional_subnode_w_validator_value_not_exists(self):
+        from colander import MappingSchema
+        from colander import SchemaNode
+        from colander import Integer
+        from colander import Range
+        class Schema(MappingSchema):
+            optional = SchemaNode(Integer(), validator=Range(1), optional=True)
+        schema = Schema()
+        result = schema.deserialize({})
+        self.assertEqual(result, {})
+
 class TestTuple(unittest.TestCase):
     def _makeOne(self):
         from colander import Tuple
@@ -1865,6 +1919,7 @@ class DummySchemaNode(object):
         self.required = default is None
         self.default = default
         self.children = []
+        self.optional = False
 
     def deserialize(self, val):
         from colander import Invalid
