diff --git a/marshmallow_jsonschema/validation.py b/marshmallow_jsonschema/validation.py index e0a9c9b..d38c818 100644 --- a/marshmallow_jsonschema/validation.py +++ b/marshmallow_jsonschema/validation.py @@ -53,7 +53,7 @@ def handle_length(schema, field, validator, parent_schema): def handle_one_of(schema, field, validator, parent_schema): """Adds the validation logic for ``marshmallow.validate.OneOf`` by setting - the JSONSchema `enum` property to the allowed choices in the validator. + the JSONSchema `oneOf` property to the allowed choices in the validator. Args: schema (dict): The original JSON schema we generated. This is what we @@ -69,8 +69,23 @@ def handle_one_of(schema, field, validator, parent_schema): dict: New JSON Schema that has been post processed and altered. """ - schema["enum"] = list(validator.choices) - schema["enumNames"] = list(validator.labels) + if schema["type"] not in ["string", "number"]: + return schema + + if "oneOf" in schema: + return schema + + choices = [ + field._serialize(choice, field.name, None) for choice in validator.choices + ] + if not choices: + return schema + + labels = validator.labels if validator.labels else choices + schema["oneOf"] = [ + {"type": schema["type"], "title": label, "const": choice} + for choice, label in zip(choices, labels) + ] return schema diff --git a/tests/test_dump.py b/tests/test_dump.py index bfb5db6..151753c 100644 --- a/tests/test_dump.py +++ b/tests/test_dump.py @@ -412,8 +412,10 @@ class TestSchema(Schema): dumped = validate_and_dump(schema) assert dumped["definitions"]["TestSchema"]["properties"]["foo"] == { - "enum": [v for v in mapping.values()], - "enumNames": [k for k in mapping.keys()], + "oneOf": [ + {"type": "number", "title": k, "const": v} + for k, v in zip(mapping.keys(), mapping.values()) + ], "format": "integer", "title": "foo", "type": "number", diff --git a/tests/test_validation.py b/tests/test_validation.py index 96d7bec..9a72e20 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -40,17 +40,11 @@ def test_one_of_validator(): dumped = validate_and_dump(schema) - assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["enum"] == [ - "male", - "female", - "non_binary", - "other", - ] - assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["enumNames"] == [ - "Male", - "Female", - "Non-binary/fluid", - "Other", + assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["oneOf"] == [ + {"type": "string", "title": "Male", "const": "male"}, + {"type": "string", "title": "Female", "const": "female"}, + {"type": "string", "title": "Non-binary/fluid", "const": "non_binary"}, + {"type": "string", "title": "Other", "const": "other"}, ] @@ -63,8 +57,35 @@ class TestSchema(Schema): dumped = validate_and_dump(schema) foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"] - assert foo_property["enum"] == [] - assert foo_property["enumNames"] == [] + assert "oneOf" not in foo_property + + +def test_one_of_object(): + class TestSchema(Schema): + foo = fields.Dict(validate=OneOf([{"a": 1}])) + + schema = TestSchema() + + dumped = validate_and_dump(schema) + + foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"] + assert "oneOf" not in foo_property + + +def test_one_of_custom_field(): + class CustomField(fields.String): + def _jsonschema_type_mapping(self): + return {"type": "string", "oneOf": [{"const": "one"}, {"const": "two"}]} + + class TestSchema(Schema): + foo = CustomField(validate=OneOf(["one", "two"])) + + schema = TestSchema() + + dumped = validate_and_dump(schema) + + foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"] + assert foo_property["oneOf"] == [{"const": "one"}, {"const": "two"}] @pytest.mark.skipif(MARSHMALLOW_3, reason="marshmallow 2 only")