Created
March 13, 2017 04:52
-
-
Save bew/2af1c4c4bd8e03a5247fba9eeb83a6a9 to your computer and use it in GitHub Desktop.
A JSON.mapper for crystal that allow additional fields
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module JSON | |
# JSON.mapping documentation removed, as it's taking to much spaces :p | |
# prop: the properties, like JSON.mapping | |
# decl: whatever or not to generate the variables declarations, getters & setters | |
# more_init: if not nil, must be a TupleLiteral that is the list of additional fields to the initialize method | |
macro mapper(props properties, strict = false, decl gen_declarations = true, more_init = nil) | |
{% for key, value in properties %} | |
{% properties[key] = {type: value} unless value.is_a?(HashLiteral) || value.is_a?(NamedTupleLiteral) %} | |
{% end %} | |
{% if gen_declarations %} | |
{% for key, value in properties %} | |
@{{key.id}} : {{value[:type]}} {{ (value[:nilable] ? "?" : "").id }} | |
{% if value[:setter] == nil ? true : value[:setter] %} | |
def {{key.id}}=(_{{key.id}} : {{value[:type]}} {{ (value[:nilable] ? "?" : "").id }}) | |
@{{key.id}} = _{{key.id}} | |
end | |
{% end %} | |
{% if value[:getter] == nil ? true : value[:getter] %} | |
def {{key.id}} | |
@{{key.id}} | |
end | |
{% end %} | |
{% end %} | |
{% end %} | |
def initialize(%pull : ::JSON::PullParser, | |
{% if more_init && more_init.is_a?(TupleLiteral) && more_init.size > 0 %} | |
{% for var in more_init %} | |
@{{var.id}} | |
{% end %} | |
{% end %} | |
) | |
{% for key, value in properties %} | |
%var{key.id} = nil | |
%found{key.id} = false | |
{% end %} | |
%pull.read_object do |key| | |
case key | |
{% for key, value in properties %} | |
when {{value[:key] || key.id.stringify}} | |
%found{key.id} = true | |
%var{key.id} = | |
{% if value[:nilable] || value[:default] != nil %} %pull.read_null_or { {% end %} | |
{% if value[:root] %} | |
%pull.on_key!({{value[:root]}}) do | |
{% end %} | |
{% if value[:converter] %} | |
{{value[:converter]}}.from_json(%pull) | |
{% elsif value[:type].is_a?(Path) || value[:type].is_a?(Generic) %} | |
{{value[:type]}}.new(%pull) | |
{% else %} | |
::Union({{value[:type]}}).new(%pull) | |
{% end %} | |
{% if value[:root] %} | |
end | |
{% end %} | |
{% if value[:nilable] || value[:default] != nil %} } {% end %} | |
{% end %} | |
else | |
{% if strict %} | |
raise ::JSON::ParseException.new("Unknown json attribute: #{key}", 0, 0) | |
{% else %} | |
%pull.skip | |
{% end %} | |
end | |
end | |
{% for key, value in properties %} | |
{% unless value[:nilable] || value[:default] != nil %} | |
if %var{key.id}.nil? && !%found{key.id} && !::Union({{value[:type]}}).nilable? | |
raise ::JSON::ParseException.new("Missing json attribute: {{(value[:key] || key).id}}", 0, 0) | |
end | |
{% end %} | |
{% end %} | |
{% for key, value in properties %} | |
{% if value[:nilable] %} | |
{% if value[:default] != nil %} | |
@{{key.id}} = %found{key.id} ? %var{key.id} : {{value[:default]}} | |
{% else %} | |
@{{key.id}} = %var{key.id} | |
{% end %} | |
{% elsif value[:default] != nil %} | |
@{{key.id}} = %var{key.id}.nil? ? {{value[:default]}} : %var{key.id} | |
{% else %} | |
@{{key.id}} = (%var{key.id}).as({{value[:type]}}) | |
{% end %} | |
{% end %} | |
end | |
def to_json(json : ::JSON::Builder) | |
json.object do | |
{% for key, value in properties %} | |
_{{key.id}} = @{{key.id}} | |
{% unless value[:emit_null] %} | |
unless _{{key.id}}.nil? | |
{% end %} | |
json.field({{value[:key] || key.id.stringify}}) do | |
{% if value[:root] %} | |
{% if value[:emit_null] %} | |
if _{{key.id}}.nil? | |
nil.to_json(json) | |
else | |
{% end %} | |
json.object do | |
json.field({{value[:root]}}) do | |
{% end %} | |
{% if value[:converter] %} | |
if _{{key.id}} | |
{{ value[:converter] }}.to_json(_{{key.id}}, json) | |
else | |
nil.to_json(json) | |
end | |
{% else %} | |
_{{key.id}}.to_json(json) | |
{% end %} | |
{% if value[:root] %} | |
{% if value[:emit_null] %} | |
end | |
{% end %} | |
end | |
end | |
{% end %} | |
end | |
{% unless value[:emit_null] %} | |
end | |
{% end %} | |
{% end %} | |
end | |
end | |
end | |
# This is a convenience method to allow invoking `JSON.mapping` | |
# with named arguments instead of with a hash/named-tuple literal. | |
macro mapper(**properties) | |
::JSON.mapper({{properties}}) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment