diff --git a/spec/std/json/serializable_spec.cr b/spec/std/json/serializable_spec.cr index 4eba9cc9fde0..9d9b00725fa6 100644 --- a/spec/std/json/serializable_spec.cr +++ b/spec/std/json/serializable_spec.cr @@ -471,6 +471,25 @@ class JSONSomething property value : JSONSomething? end +module JsonDiscriminatorBug + abstract class Base + include JSON::Serializable + + use_json_discriminator("type", {"a" => A, "b" => B, "c" => C}) + end + + class A < Base + end + + class B < Base + property source : Base + property value : Int32 = 1 + end + + class C < B + end +end + describe "JSON mapping" do it "works with record" do JSONAttrPoint.new(1, 2).to_json.should eq "{\"x\":1,\"y\":2}" @@ -1104,6 +1123,14 @@ describe "JSON mapping" do bar.x.should be_a(JSONStrictDiscriminatorFoo) bar.y.should be_a(JSONStrictDiscriminatorFoo) end + + it "deserializes with discriminator, another recursive type, fixes: #13429" do + c = JsonDiscriminatorBug::Base.from_json %q({"type": "c", "source": {"type": "a"}, "value": 2}) + c.as(JsonDiscriminatorBug::C).value.should eq 2 + + c = JsonDiscriminatorBug::Base.from_json %q({"type": "c", "source": {"type": "a"}}) + c.as(JsonDiscriminatorBug::C).value.should eq 1 + end end describe "namespaced classes" do diff --git a/spec/std/yaml/serializable_spec.cr b/spec/std/yaml/serializable_spec.cr index 9050381d45f4..0ea0117eb993 100644 --- a/spec/std/yaml/serializable_spec.cr +++ b/spec/std/yaml/serializable_spec.cr @@ -404,6 +404,25 @@ class YAMLSomething property value : YAMLSomething? end +module YAMLDiscriminatorBug + abstract class Base + include YAML::Serializable + + use_yaml_discriminator("type", {"a" => A, "b" => B, "c" => C}) + end + + class A < Base + end + + class B < Base + property source : Base + property value : Int32 = 1 + end + + class C < B + end +end + describe "YAML::Serializable" do it "works with record" do YAMLAttrPoint.new(1, 2).to_yaml.should eq "---\nx: 1\ny: 2\n" @@ -1005,6 +1024,14 @@ describe "YAML::Serializable" do bar.x.should be_a(YAMLStrictDiscriminatorFoo) bar.y.should be_a(YAMLStrictDiscriminatorFoo) end + + it "deserializes with discriminator, another recursive type, fixes: #13429" do + c = YAMLDiscriminatorBug::Base.from_yaml %q({"type": "c", "source": {"type": "a"}, "value": 2}) + c.as(YAMLDiscriminatorBug::C).value.should eq 2 + + c = YAMLDiscriminatorBug::Base.from_yaml %q({"type": "c", "source": {"type": "a"}}) + c.as(YAMLDiscriminatorBug::C).value.should eq 1 + end end describe "namespaced classes" do diff --git a/src/json/serialization.cr b/src/json/serialization.cr index c45f4119a3e4..5bfe9af36f34 100644 --- a/src/json/serialization.cr +++ b/src/json/serialization.cr @@ -190,6 +190,7 @@ module JSON has_default: ivar.has_default_value?, default: ivar.default_value, nilable: ivar.type.nilable?, + type: ivar.type, root: ann && ann[:root], converter: ann && ann[:converter], presence: ann && ann[:presence], @@ -202,9 +203,9 @@ module JSON # recursively defined serializable types {% for name, value in properties %} %var{name} = {% if value[:has_default] || value[:nilable] %} - nil.as(::Nil | typeof(@{{name}})) + nil.as(::Union(::Nil, {{value[:type]}})) {% else %} - uninitialized typeof(@{{name}}) + uninitialized ::Union({{value[:type]}}) {% end %} %found{name} = false {% end %} @@ -228,7 +229,7 @@ module JSON {% if value[:converter] %} {{value[:converter]}}.from_json(pull) {% else %} - typeof(@{{name}}).new(pull) + ::Union({{value[:type]}}).new(pull) {% end %} end end diff --git a/src/yaml/serialization.cr b/src/yaml/serialization.cr index ebceb13529ec..446fdfde6da5 100644 --- a/src/yaml/serialization.cr +++ b/src/yaml/serialization.cr @@ -196,6 +196,7 @@ module YAML has_default: ivar.has_default_value?, default: ivar.default_value, nilable: ivar.type.nilable?, + type: ivar.type, converter: ann && ann[:converter], presence: ann && ann[:presence], } @@ -207,9 +208,9 @@ module YAML # recursively defined serializable types {% for name, value in properties %} %var{name} = {% if value[:has_default] || value[:nilable] %} - nil.as(::Nil | typeof(@{{name}})) + nil.as(::Union(::Nil, {{value[:type]}})) {% else %} - uninitialized typeof(@{{name}}) + uninitialized ::Union({{value[:type]}}) {% end %} %found{name} = false {% end %} @@ -232,7 +233,7 @@ module YAML {% if value[:converter] %} {{value[:converter]}}.from_yaml(ctx, value_node) {% else %} - typeof(@{{name}}).new(ctx, value_node) + ::Union({{value[:type]}}).new(ctx, value_node) {% end %} end