Skip to content

Commit

Permalink
Showing 4 changed files with 31 additions and 21 deletions.
4 changes: 1 addition & 3 deletions spec/std/json/mapping_spec.cr
Original file line number Diff line number Diff line change
@@ -28,9 +28,7 @@ class JSONPersonEmittingNull
end

class JSONWithBool
JSON.mapping({
value: {type: Bool},
})
JSON.mapping value: Bool
end

class JSONWithTime
4 changes: 1 addition & 3 deletions spec/std/yaml/mapping_spec.cr
Original file line number Diff line number Diff line change
@@ -21,9 +21,7 @@ class StrictYAMLPerson
end

class YAMLWithBool
YAML.mapping({
value: {type: Bool},
})
YAML.mapping value: Bool
end

class YAMLWithTime
21 changes: 14 additions & 7 deletions src/json/mapping.cr
Original file line number Diff line number Diff line change
@@ -7,17 +7,17 @@ module JSON
# require "json"
#
# class Location
# JSON.mapping({
# JSON.mapping(
# lat: Float64,
# lng: Float64,
# })
# )
# end
#
# class House
# JSON.mapping({
# address: String,
# JSON.mapping(
# address: String,
# location: {type: Location, nilable: true},
# })
# )
# end
#
# house = House.from_json(%({"address": "Crystal Road 1234", "location": {"lat": 12.3, "lng": 34.5}}))
@@ -28,7 +28,8 @@ module JSON
#
# ### Usage
#
# `JSON.mapping` must receive a hash literal whose keys will define Crystal properties.
# `JSON.mapping` must receive a series of named arguments, or a named tuple literal, or a hash literal,
# whose keys will define Crystal properties.
#
# The value of each key can be a single type (not a union type). Primitive types (numbers, string, boolean and nil)
# are supported, as well as custom objects which use `JSON.mapping` or define a `new` method
@@ -37,7 +38,7 @@ module JSON
# The value can also be another hash literal with the following options:
# * **type**: (required) the single type described above (you can use `JSON::Any` too)
# * **key**: the property name in the JSON document (as opposed to the property name in the Crystal code)
# * **nilable**: if true, the property can be `Nil`
# * **nilable**: if true, the property can be `Nil`. Passing `T | Nil` as a type has the same effect.
# * **default**: value to use if the property is missing in the JSON document, or if it's `null` and `nilable` was not set to `true`. If the default value creates a new instance of an object (for example `[1, 2, 3]` or `SomeObject.new`), a different instance will be used each time a JSON document is parsed.
# * **emit_null**: if true, emits a `null` value for nilable properties (by default nulls are not emitted)
# * **converter**: specify an alternate type for parsing and generation. The converter must define `from_json(JSON::PullParser)` and `to_json(value, IO)` as class methods. Examples of converters are `Time::Format` and `Time::EpochConverter` for `Time`.
@@ -189,4 +190,10 @@ module JSON
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 mapping(**properties)
::JSON.mapping({{properties}})
end
end
23 changes: 15 additions & 8 deletions src/yaml/mapping.cr
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
module YAML
# The `YAML.mapping` macro defines how an object is mapped to YAML.
#
# It takes hash literal as argument, in which attributes and types are defined.
# It takes named arguments, a named tuple literal or a hash literal as argument,
# in which attributes and types are defined.
# Once defined, `Object#from_yaml` populates properties of the class from the
# YAML document.
#
# ```crystal
# require "yaml"
#
# class Employee
# YAML.mapping({
# YAML.mapping(
# title: String,
# name: String,
# })
# name: String,
# )
# end
#
# employee = Employee.from_yaml("title: Manager\nname: John")
@@ -38,21 +39,21 @@ module YAML
#
# ```crystal
# class Employee
# YAML.mapping({
# YAML.mapping(
# title: String,
# name: {
# name: {
# type: String,
# nilable: true,
# key: "firstname",
# },
# })
# )
# end
# ```
#
# Available attributes:
#
# * *type* (required) defines its type. In the example above, *title: String* is a shortcut to *title: {type: String}*.
# * *nilable* defines if a property can be a `Nil`.
# * *nilable* defines if a property can be a `Nil`. Passing `T | Nil` as a type has the same effect.
# * **default**: value to use if the property is missing in the YAML document, or if it's `null` and `nilable` was not set to `true`. If the default value creates a new instance of an object (for example `[1, 2, 3]` or `SomeObject.new`), a different instance will be used each time a YAML document is parsed.
# * *key* defines which key to read from a YAML document. It defaults to the name of the property.
# * *converter* takes an alternate type for parsing. It requires a `#from_yaml` method in that class, and returns an instance of the given type. Examples of converters are `Time::Format` and `Time::EpochConverter` for `Time`.
@@ -139,4 +140,10 @@ module YAML
{% end %}
end
end

# This is a convenience method to allow invoking `YAML.mapping`
# with named arguments instead of with a hash/named-tuple literal.
macro mapping(**properties)
::YAML.mapping({{properties}})
end
end

0 comments on commit 251694d

Please sign in to comment.