Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add json mapping extra fields #6009

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions spec/std/json/mapping_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ private class JSONPersonEmittingNull
})
end

private class JSONPersonWithExtra
JSON.mapping({
name: {type: String},
age: {type: Int32, nilable: true},
}, extra: "other")
end

private class JSONWithBool
JSON.mapping value: Bool
end
Expand Down Expand Up @@ -278,6 +285,21 @@ describe "JSON mapping" do
ex.location.should eq({3, 15})
end

it "should unpack extra fields" do
person = JSONPersonWithExtra.from_json(%({"name": "John", "age": 30, "extra1": 1, "extra2": [1,2,3]}))
person.name.should eq("John")
person.age.should eq(30)
person.other["extra1"].should eq 1
person.other["extra2"].should eq [1, 2, 3]
end

it "should pack extra fields" do
person = JSONPersonWithExtra.from_json(%({"name": "John", "age": 30, "extra1": 1, "extra2": [1,2,3]}))
person.other["extra3"] = JSON::Any.new("bla")
person.other.delete("extra1")
person.to_json.should eq "{\"name\":\"John\",\"age\":30,\"extra2\":[1,2,3],\"extra3\":\"bla\"}"
end

it "doesn't emit null by default when doing to_json" do
person = JSONPerson.from_json(%({"name": "John"}))
(person.to_json =~ /age/).should be_falsey
Expand Down
25 changes: 24 additions & 1 deletion src/json/mapping.cr
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,14 @@ module JSON
# If *strict* is `true`, unknown properties in the JSON
# document will raise a parse exception. The default is `false`, so unknown properties
# are silently ignored.
macro mapping(_properties_, strict = false)
#
# If *extra* is a String, unknown properties in the JSON
# document will be stored into field with this name.
macro mapping(_properties_, strict = false, extra = nil)
{% if extra && _properties_.keys.includes? extra.id %}
{{ raise "Name for extra property already in use: #{extra.id}" }}
{% end %}

{% for key, value in _properties_ %}
{% _properties_[key] = {type: value} unless value.is_a?(HashLiteral) || value.is_a?(NamedTupleLiteral) %}
{% end %}
Expand Down Expand Up @@ -98,6 +105,14 @@ module JSON
{% end %}
{% end %}

{% if extra %}
@{{extra.id}} = Hash(String, ::JSON::Any).new

def {{extra.id}}
@{{extra.id}}
end
{% end %}

def initialize(%pull : ::JSON::PullParser)
{% for key, value in _properties_ %}
%var{key.id} = nil
Expand Down Expand Up @@ -145,6 +160,8 @@ module JSON
else
{% if strict %}
raise ::JSON::MappingError.new("Unknown JSON attribute: #{key}", self.class, *%key_location)
{% elsif extra %}
@{{extra.id}}[key] = ::JSON::Any.new(%pull)
{% else %}
%pull.skip
{% end %}
Expand Down Expand Up @@ -221,6 +238,12 @@ module JSON
end
{% end %}
{% end %}

{% if extra %}
@{{extra.id}}.each do |key, obj|
json.field(key) { obj.to_json(json) }
end
{% end %}
end
end
end
Expand Down