Repository: marshmallow-code/marshmallow

Issue:
Empty string data_key disallowed
Having an empty string as `data_key` doesn't seem to work.

```py
import marshmallow
from marshmallow import fields


class RootSchema(marshmallow.Schema):
    x = fields.Raw(data_key="")


RootSchema().load({'': 1})

```
```py

---> 14 RootSchema().load({'': 1})

~/.pyenv/versions/3.7.3/lib/python3.7/site-packages/marshmallow/schema.py in load(self, data, many, partial, unknown)
    730         """
    731         return self._do_load(
--> 732             data, many=many, partial=partial, unknown=unknown, postprocess=True
    733         )
    734 

~/.pyenv/versions/3.7.3/lib/python3.7/site-packages/marshmallow/schema.py in _do_load(self, data, many, partial, unknown, postprocess)
    892             exc = ValidationError(errors, data=data, valid_data=result)
    893             self.handle_error(exc, data)
--> 894             raise exc
    895 
    896         return result

ValidationError: {'': ['Unknown field.']}
```

marshmallow-3.0.2

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 002562ee..60ea4ee9 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,14 @@
 Changelog
 ---------
 
+3.0.3 (unreleased)
+++++++++++++++++++
+
+Bug fixes:
+
+- Handle when ``data_key`` is an empty string (:issue:`1378`).
+  Thanks :user:`jtrakk` for reporting.
+
 3.0.2 (2019-09-04)
 ++++++++++++++++++
 
diff --git a/src/marshmallow/schema.py b/src/marshmallow/schema.py
index 9771d261..afd2f5db 100644
--- a/src/marshmallow/schema.py
+++ b/src/marshmallow/schema.py
@@ -503,7 +503,7 @@ class BaseSchema(base.SchemaABC):
             value = field_obj.serialize(attr_name, obj, accessor=self.get_attribute)
             if value is missing:
                 continue
-            key = field_obj.data_key or attr_name
+            key = field_obj.data_key if field_obj.data_key is not None else attr_name
             ret[key] = value
         return ret
 
@@ -607,7 +607,9 @@ class BaseSchema(base.SchemaABC):
         else:
             partial_is_collection = is_collection(partial)
             for attr_name, field_obj in self.load_fields.items():
-                field_name = field_obj.data_key or attr_name
+                field_name = (
+                    field_obj.data_key if field_obj.data_key is not None else attr_name
+                )
                 raw_value = data.get(field_name, missing)
                 if raw_value is missing:
                     # Ignore missing field if we're allowed to.
@@ -641,7 +643,7 @@ class BaseSchema(base.SchemaABC):
                     set_value(ret, key, value)
             if unknown != EXCLUDE:
                 fields = {
-                    field_obj.data_key or field_name
+                    field_obj.data_key if field_obj.data_key is not None else field_name
                     for field_name, field_obj in self.load_fields.items()
                 }
                 for key in set(data) - fields:
@@ -912,9 +914,9 @@ class BaseSchema(base.SchemaABC):
             fields_dict[field_name] = field_obj
 
         dump_data_keys = [
-            obj.data_key or name
-            for name, obj in fields_dict.items()
-            if not obj.load_only
+            field_obj.data_key if field_obj.data_key is not None else name
+            for name, field_obj in fields_dict.items()
+            if not field_obj.load_only
         ]
         if len(dump_data_keys) != len(set(dump_data_keys)):
             data_keys_duplicates = {
@@ -1029,6 +1031,9 @@ class BaseSchema(base.SchemaABC):
                     '"{}" field does not exist.'.format(field_name)
                 ) from error
 
+            data_key = (
+                field_obj.data_key if field_obj.data_key is not None else field_name
+            )
             if many:
                 for idx, item in enumerate(data):
                     try:
@@ -1039,7 +1044,7 @@ class BaseSchema(base.SchemaABC):
                         validated_value = self._call_and_store(
                             getter_func=validator,
                             data=value,
-                            field_name=field_obj.data_key or field_name,
+                            field_name=data_key,
                             error_store=error_store,
                             index=(idx if self.opts.index_errors else None),
                         )
@@ -1054,7 +1059,7 @@ class BaseSchema(base.SchemaABC):
                     validated_value = self._call_and_store(
                         getter_func=validator,
                         data=value,
-                        field_name=field_obj.data_key or field_name,
+                        field_name=data_key,
                         error_store=error_store,
                     )
                     if validated_value is missing:


Test patch:
diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py
index 7536cf75..30fc0a3e 100644
--- a/tests/test_deserialization.py
+++ b/tests/test_deserialization.py
@@ -1285,6 +1285,13 @@ class TestSchemaDeserialization:
         assert result["email"] == "foo@bar.com"
         assert "years" not in result
 
+    def test_deserialize_with_data_key_as_empty_string(self):
+        class MySchema(Schema):
+            name = fields.Field(data_key="")
+
+        schema = MySchema()
+        assert schema.load({"": "Grace"}) == {"name": "Grace"}
+
     def test_deserialize_with_dump_only_param(self):
         class AliasingUserSerializer(Schema):
             name = fields.String()
diff --git a/tests/test_serialization.py b/tests/test_serialization.py
index 56952c20..f88e28af 100644
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -390,6 +390,13 @@ class TestFieldSerialization:
         result = DumpToSchema().dump(data)
         assert result == {"NamE": "Richard", "YearS": 11}
 
+    def test_serialize_with_data_key_as_empty_string(self):
+        class MySchema(Schema):
+            name = fields.Field(data_key="")
+
+        schema = MySchema()
+        schema.dump({"name": "Grace"}) == {"": "Grace"}
+
     def test_serialize_with_attribute_and_data_key_uses_data_key(self):
         class ConfusedDumpToAndAttributeSerializer(Schema):
             name = fields.String(data_key="FullName")