Strict by default and implement Serializable::Unmapped
parent
2f8693ca34
commit
9d4c37460c
|
@ -23,6 +23,19 @@ class ExampleC
|
||||||
property b : String
|
property b : String
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ExampleStrict
|
||||||
|
include CBOR::Serializable
|
||||||
|
|
||||||
|
property a : Int32
|
||||||
|
end
|
||||||
|
|
||||||
|
class ExampleUnmapped
|
||||||
|
include CBOR::Serializable
|
||||||
|
include CBOR::Serializable::Unmapped
|
||||||
|
|
||||||
|
property a : Int32
|
||||||
|
end
|
||||||
|
|
||||||
describe CBOR::Serializable do
|
describe CBOR::Serializable do
|
||||||
describe "rfc examples" do
|
describe "rfc examples" do
|
||||||
describe %(example {_ "a": 1, "b": [_ 2, 3]}) do
|
describe %(example {_ "a": 1, "b": [_ 2, 3]}) do
|
||||||
|
@ -54,4 +67,21 @@ describe CBOR::Serializable do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "by default it's strict" do
|
||||||
|
it "errors on missing fields" do
|
||||||
|
expect_raises(CBOR::ParseError) do
|
||||||
|
ExampleStrict.from_cbor(Bytes[0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe CBOR::Serializable::Unmapped do
|
||||||
|
it "adds missing fields to the map" do
|
||||||
|
result = ExampleUnmapped.from_cbor(Bytes[0xbf, 0x61, 0x61, 0x01, 0x61, 0x62, 0x9f, 0x02, 0x03, 0xff, 0xff])
|
||||||
|
|
||||||
|
result.a.should eq(1)
|
||||||
|
result.cbor_unmapped.should eq({"b" => [2, 3]})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,5 +21,7 @@ module CBOR
|
||||||
UInt32 |
|
UInt32 |
|
||||||
Int64 |
|
Int64 |
|
||||||
UInt64 |
|
UInt64 |
|
||||||
Int128
|
Int128 |
|
||||||
|
Float32 |
|
||||||
|
Float64
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,40 @@ class CBOR::Decoder
|
||||||
@current_token = @lexer.next_token
|
@current_token = @lexer.next_token
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def read_value : Type
|
||||||
|
case token = @current_token
|
||||||
|
when Token::TagT
|
||||||
|
finish_token!
|
||||||
|
read_value
|
||||||
|
when Token::StringT
|
||||||
|
finish_token!
|
||||||
|
token.value
|
||||||
|
when Token::IntT
|
||||||
|
finish_token!
|
||||||
|
token.value
|
||||||
|
when Token::FloatT
|
||||||
|
finish_token!
|
||||||
|
token.value
|
||||||
|
when Token::BytesT
|
||||||
|
finish_token!
|
||||||
|
token.value
|
||||||
|
when Token::SimpleValueT
|
||||||
|
token.value.to_t
|
||||||
|
when Token::ArrayT
|
||||||
|
finish_token!
|
||||||
|
arr = Array(Type).new
|
||||||
|
consume_sequence(token.size) { arr << read_value }
|
||||||
|
arr
|
||||||
|
when Token::MapT
|
||||||
|
finish_token!
|
||||||
|
map = Hash(Type, Type).new
|
||||||
|
consume_sequence(token.size) { map[read_value] = read_value }
|
||||||
|
map
|
||||||
|
else
|
||||||
|
unexpected_token(token)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def read_string : String
|
def read_string : String
|
||||||
case token = @current_token
|
case token = @current_token
|
||||||
when Token::StringT
|
when Token::StringT
|
||||||
|
@ -91,23 +125,6 @@ class CBOR::Decoder
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip
|
|
||||||
case token = @current_token
|
|
||||||
when Token::IntT,
|
|
||||||
Token::FloatT,
|
|
||||||
Token::BytesT,
|
|
||||||
Token::StringT,
|
|
||||||
Token::TagT,
|
|
||||||
Token::SimpleValueT
|
|
||||||
finish_token!
|
|
||||||
when Token::ArrayT, Token::MapT
|
|
||||||
finish_token!
|
|
||||||
consume_sequence(token.size) { }
|
|
||||||
else
|
|
||||||
unexpected_token(token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def finish_token!
|
def finish_token!
|
||||||
@current_token = @lexer.next_token
|
@current_token = @lexer.next_token
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,13 +79,10 @@ module CBOR
|
||||||
# A.from_json(%<{"a":1}>) # => A(@a=1, @b=1.0) #TODO ----- FIX THIS!!!!
|
# A.from_json(%<{"a":1}>) # => A(@a=1, @b=1.0) #TODO ----- FIX THIS!!!!
|
||||||
# ```
|
# ```
|
||||||
#
|
#
|
||||||
# ### Extensions: `JSON::Serializable::Strict` and `JSON::Serializable::Unmapped`.
|
# ### Extensions: `CBOR::Serializable::Unmapped`.
|
||||||
#
|
#
|
||||||
# If the `JSON::Serializable::Strict` module is included, unknown properties in the JSON
|
# If the `CBOR::Serializable::Unmapped` module is included, unknown properties in the CBOR
|
||||||
# document will raise a parse exception. By default the unknown properties
|
# document will be stored in a `Hash(String, CBOR::Type)`. On serialization, any keys inside cbor_unmapped
|
||||||
# are silently ignored.
|
|
||||||
# If the `JSON::Serializable::Unmapped` module is included, unknown properties in the JSON
|
|
||||||
# document will be stored in a `Hash(String, JSON::Any)`. On serialization, any keys inside json_unmapped
|
|
||||||
# will be serialized and appended to the current json object.
|
# will be serialized and appended to the current json object.
|
||||||
# ```
|
# ```
|
||||||
# require "json"
|
# require "json"
|
||||||
|
@ -181,9 +178,7 @@ module CBOR
|
||||||
begin
|
begin
|
||||||
decoder.read_begin_hash
|
decoder.read_begin_hash
|
||||||
rescue exc : ::CBOR::ParseError
|
rescue exc : ::CBOR::ParseError
|
||||||
# TODO: Improve error message, use dedicated class
|
raise ::CBOR::SerializationError.new(exc.message, self.class.to_s, nil)
|
||||||
raise "Error in mapping decoding when reading being hash: #{exc.message}"
|
|
||||||
# raise ::JSON::MappingError.new(exc.message, self.class.to_s, nil, *%location, exc)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
decoder.consume_hash do
|
decoder.consume_hash do
|
||||||
|
@ -204,9 +199,7 @@ module CBOR
|
||||||
|
|
||||||
{% if value[:nilable] || value[:has_default] %} } {% end %}
|
{% if value[:nilable] || value[:has_default] %} } {% end %}
|
||||||
rescue exc : ::CBOR::ParseError
|
rescue exc : ::CBOR::ParseError
|
||||||
# TODO: Improve error message, use dedicated class
|
raise ::CBOR::SerializationError.new(exc.message, self.class.to_s, {{value[:key]}})
|
||||||
raise "Error in mapping decoding when consuming hash: #{exc.message}"
|
|
||||||
# raise ::JSON::MappingError.new(exc.message, self.class.to_s, {{value[:key]}}, *%key_location, exc)
|
|
||||||
end
|
end
|
||||||
{% end %}
|
{% end %}
|
||||||
else
|
else
|
||||||
|
@ -217,9 +210,7 @@ module CBOR
|
||||||
{% for name, value in properties %}
|
{% for name, value in properties %}
|
||||||
{% unless value[:nilable] || value[:has_default] %}
|
{% unless value[:nilable] || value[:has_default] %}
|
||||||
if %var{name}.nil? && !%found{name} && !::Union({{value[:type]}}).nilable?
|
if %var{name}.nil? && !%found{name} && !::Union({{value[:type]}}).nilable?
|
||||||
# TODO: Improve error message, use dedicated class
|
raise ::CBOR::SerializationError.new("Missing CBOR attribute: {{value[:key].id}}", self.class.to_s, nil)
|
||||||
raise "Missing CBOR attribute"
|
|
||||||
# raise ::JSON::MappingError.new("Missing JSON attribute: {{value[:key].id}}", self.class.to_s, nil, *%location, nil)
|
|
||||||
end
|
end
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
|
@ -247,7 +238,7 @@ module CBOR
|
||||||
end
|
end
|
||||||
|
|
||||||
protected def on_unknown_cbor_attribute(decoder, key)
|
protected def on_unknown_cbor_attribute(decoder, key)
|
||||||
decoder.skip
|
raise ::CBOR::SerializationError.new("Unknown CBOR attribute: #{key}", self.class.to_s, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
# protected def on_to_cbor(cbor : ::CBOR::Builder)
|
# protected def on_to_cbor(cbor : ::CBOR::Builder)
|
||||||
|
@ -322,33 +313,25 @@ module CBOR
|
||||||
# {% end %}
|
# {% end %}
|
||||||
# end
|
# end
|
||||||
|
|
||||||
module Strict
|
module Unmapped
|
||||||
|
@[CBOR::Field(ignore: true)]
|
||||||
|
property cbor_unmapped = Hash(String, ::CBOR::Type).new
|
||||||
|
|
||||||
protected def on_unknown_cbor_attribute(decoder, key)
|
protected def on_unknown_cbor_attribute(decoder, key)
|
||||||
# TODO: Improve error
|
cbor_unmapped[key] = begin
|
||||||
raise "Unknown CBOR attribute: #{key}"
|
decoder.read_value
|
||||||
# raise ::JSON::MappingError.new("Unknown JSON attribute: #{key}", self.class.to_s, nil, *key_location, nil)
|
rescue exc : ::CBOR::ParseError
|
||||||
|
raise ::CBOR::SerializationError.new(exc.message, self.class.to_s, key)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# protected def on_to_json(json)
|
||||||
|
# json_unmapped.each do |key, value|
|
||||||
|
# json.field(key) { value.to_json(json) }
|
||||||
|
# end
|
||||||
|
# end
|
||||||
end
|
end
|
||||||
|
|
||||||
# module Unmapped
|
|
||||||
# @[CBOR::Field(ignore: true)]
|
|
||||||
# property cbor_unmapped = Hash(String, ::CBOR::Type).new
|
|
||||||
|
|
||||||
# protected def on_unknown_cbor_attribute(decoder, key)
|
|
||||||
# json_unmapped[key] = begin
|
|
||||||
# JSON::Any.new(pull)
|
|
||||||
# rescue exc : ::JSON::ParseException
|
|
||||||
# raise ::JSON::MappingError.new(exc.message, self.class.to_s, key, *key_location, exc)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# protected def on_to_json(json)
|
|
||||||
# json_unmapped.each do |key, value|
|
|
||||||
# json.field(key) { value.to_json(json) }
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# Tells this class to decode CBOR by using a field as a discriminator.
|
# Tells this class to decode CBOR by using a field as a discriminator.
|
||||||
#
|
#
|
||||||
# - *field* must be the field name to use as a discriminator
|
# - *field* must be the field name to use as a discriminator
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
class CBOR::SerializationError < Exception
|
||||||
|
getter klass : String
|
||||||
|
|
||||||
|
def initialize(message : String?, @klass : String, @attribute : String?)
|
||||||
|
message = String.build do |io|
|
||||||
|
io << message
|
||||||
|
io << "\n parsing "
|
||||||
|
io << klass
|
||||||
|
if attribute = @attribute
|
||||||
|
io << '#' << attribute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
super(message)
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,6 +19,18 @@ enum CBOR::SimpleValue : UInt8
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_t : Bool | Nil
|
||||||
|
case self
|
||||||
|
when False
|
||||||
|
false
|
||||||
|
when True
|
||||||
|
true
|
||||||
|
when Null,
|
||||||
|
Undefined
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def is_nil? : Bool
|
def is_nil? : Bool
|
||||||
case self
|
case self
|
||||||
when Null, Undefined
|
when Null, Undefined
|
||||||
|
|
Loading…
Reference in New Issue