From a2573f9142bec8680bd81f08739a0cd665d4ad86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20M=C3=BCller?= Date: Sun, 9 Feb 2025 19:39:56 +0100 Subject: [PATCH] Add `Union.from_json_object_key?` (#15411) --- spec/std/json/serialization_spec.cr | 17 +++++++++++++++++ src/json/from_json.cr | 22 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/spec/std/json/serialization_spec.cr b/spec/std/json/serialization_spec.cr index 80fc83e13b7e..a3dad00a7737 100644 --- a/spec/std/json/serialization_spec.cr +++ b/spec/std/json/serialization_spec.cr @@ -143,6 +143,23 @@ describe "JSON serialization" do Hash(BigDecimal, String).from_json(%({"1234567890.123456789": "x"})).should eq({"1234567890.123456789".to_big_d => "x"}) end + describe "Hash with union key (Union.from_json_object_key?)" do + it "string deprioritized" do + Hash(String | Int32, Nil).from_json(%({"1": null})).should eq({1 => nil}) + Hash(String | UInt32, Nil).from_json(%({"1": null})).should eq({1 => nil}) + end + + it "string without alternative" do + Hash(String | Int32, Nil).from_json(%({"foo": null})).should eq({"foo" => nil}) + end + + it "no match" do + expect_raises JSON::ParseException, %(Can't convert "foo" into (Float64 | Int32) at line 1, column 2) do + Hash(Float64 | Int32, Nil).from_json(%({"foo": null})) + end + end + end + it "raises an error Hash(String, Int32)#from_json with null value" do expect_raises(JSON::ParseException, "Expected Int but was Null") do Hash(String, Int32).from_json(%({"foo": 1, "bar": 2, "baz": null})) diff --git a/src/json/from_json.cr b/src/json/from_json.cr index 1c6a9e3c9c29..92edf0472c77 100644 --- a/src/json/from_json.cr +++ b/src/json/from_json.cr @@ -440,6 +440,28 @@ def Union.new(pull : JSON::PullParser) {% end %} end +def Union.from_json_object_key?(key : String) + {% begin %} + # String must come last because any key can be parsed into a String. + # So, we give a chance first to other types in the union to be parsed. + {% string_type = T.find { |type| type == ::String } %} + + {% for type in T %} + {% unless type == string_type %} + if result = {{ type }}.from_json_object_key?(key) + return result + end + {% end %} + {% end %} + + {% if string_type %} + if result = {{ string_type }}.from_json_object_key?(key) + return result + end + {% end %} + {% end %} +end + # Reads a string from JSON parser as a time formatted according to [RFC 3339](https://tools.ietf.org/html/rfc3339) # or other variations of [ISO 8601](http://xml.coverpages.org/ISO-FDIS-8601.pdf). #