Skip to content

Commit 0b4d250

Browse files
committed
fix(validation): handle_one_of uses oneOf instead of enum
enumNames is not part of the JSON Schema spec, but it is possible to get the same behaviour using a oneOf with titles
1 parent c032d1a commit 0b4d250

File tree

3 files changed

+56
-18
lines changed

3 files changed

+56
-18
lines changed

marshmallow_jsonschema/validation.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def handle_length(schema, field, validator, parent_schema):
5353

5454
def handle_one_of(schema, field, validator, parent_schema):
5555
"""Adds the validation logic for ``marshmallow.validate.OneOf`` by setting
56-
the JSONSchema `enum` property to the allowed choices in the validator.
56+
the JSONSchema `oneOf` property to the allowed choices in the validator.
5757
5858
Args:
5959
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):
6969
dict: New JSON Schema that has been post processed and
7070
altered.
7171
"""
72-
schema["enum"] = list(validator.choices)
73-
schema["enumNames"] = list(validator.labels)
72+
if schema["type"] not in ["string", "number"]:
73+
return schema
74+
75+
if "oneOf" in schema:
76+
return schema
77+
78+
choices = [
79+
field._serialize(choice, field.name, None) for choice in validator.choices
80+
]
81+
if not choices:
82+
return schema
83+
84+
labels = validator.labels if validator.labels else choices
85+
schema["oneOf"] = [
86+
{"type": schema["type"], "title": label, "const": choice}
87+
for choice, label in zip(choices, labels)
88+
]
7489

7590
return schema
7691

tests/test_dump.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,10 @@ class TestSchema(Schema):
486486
dumped = validate_and_dump(schema)
487487

488488
assert dumped["definitions"]["TestSchema"]["properties"]["foo"] == {
489-
"enum": [v for v in mapping.values()],
490-
"enumNames": [k for k in mapping.keys()],
489+
"oneOf": [
490+
{"type": "number", "title": k, "const": v}
491+
for k, v in zip(mapping.keys(), mapping.values())
492+
],
491493
"format": "integer",
492494
"title": "foo",
493495
"type": "number",

tests/test_validation.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,11 @@ def test_one_of_validator():
5353

5454
dumped = validate_and_dump(schema)
5555

56-
assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["enum"] == [
57-
"male",
58-
"female",
59-
"non_binary",
60-
"other",
61-
]
62-
assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["enumNames"] == [
63-
"Male",
64-
"Female",
65-
"Non-binary/fluid",
66-
"Other",
56+
assert dumped["definitions"]["UserSchema"]["properties"]["sex"]["oneOf"] == [
57+
{"type": "string", "title": "Male", "const": "male"},
58+
{"type": "string", "title": "Female", "const": "female"},
59+
{"type": "string", "title": "Non-binary/fluid", "const": "non_binary"},
60+
{"type": "string", "title": "Other", "const": "other"},
6761
]
6862

6963

@@ -76,8 +70,35 @@ class TestSchema(Schema):
7670
dumped = validate_and_dump(schema)
7771

7872
foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"]
79-
assert foo_property["enum"] == []
80-
assert foo_property["enumNames"] == []
73+
assert "oneOf" not in foo_property
74+
75+
76+
def test_one_of_object():
77+
class TestSchema(Schema):
78+
foo = fields.Dict(validate=OneOf([{"a": 1}]))
79+
80+
schema = TestSchema()
81+
82+
dumped = validate_and_dump(schema)
83+
84+
foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"]
85+
assert "oneOf" not in foo_property
86+
87+
88+
def test_one_of_custom_field():
89+
class CustomField(fields.String):
90+
def _jsonschema_type_mapping(self):
91+
return {"type": "string", "oneOf": [{"const": "one"}, {"const": "two"}]}
92+
93+
class TestSchema(Schema):
94+
foo = CustomField(validate=OneOf(["one", "two"]))
95+
96+
schema = TestSchema()
97+
98+
dumped = validate_and_dump(schema)
99+
100+
foo_property = dumped["definitions"]["TestSchema"]["properties"]["foo"]
101+
assert foo_property["oneOf"] == [{"const": "one"}, {"const": "two"}]
81102

82103

83104
def test_range():

0 commit comments

Comments
 (0)