Strict by default and implement Serializable::Unmapped
parent
2f8693ca34
commit
9d4c37460c
|
@ -23,6 +23,19 @@ class ExampleC
|
|||
property b : String
|
||||
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 "rfc examples" do
|
||||
describe %(example {_ "a": 1, "b": [_ 2, 3]}) do
|
||||
|
@ -54,4 +67,21 @@ describe CBOR::Serializable do
|
|||
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
|
||||
|
|
|
@ -21,5 +21,7 @@ module CBOR
|
|||
UInt32 |
|
||||
Int64 |
|
||||
UInt64 |
|
||||
Int128
|
||||
Int128 |
|
||||
Float32 |
|
||||
Float64
|
||||
end
|
||||
|
|
|
@ -7,6 +7,40 @@ class CBOR::Decoder
|
|||
@current_token = @lexer.next_token
|
||||
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
|
||||
case token = @current_token
|
||||
when Token::StringT
|
||||
|
@ -91,23 +125,6 @@ class CBOR::Decoder
|
|||
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!
|
||||
@current_token = @lexer.next_token
|
||||
end
|
||||
|
|
|
@ -79,13 +79,10 @@ module CBOR
|
|||
# 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
|
||||
# document will raise a parse exception. By default the unknown properties
|
||||
# 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
|
||||
# If the `CBOR::Serializable::Unmapped` module is included, unknown properties in the CBOR
|
||||
# document will be stored in a `Hash(String, CBOR::Type)`. On serialization, any keys inside cbor_unmapped
|
||||
# will be serialized and appended to the current json object.
|
||||
# ```
|
||||
# require "json"
|
||||
|
@ -181,9 +178,7 @@ module CBOR
|
|||
begin
|
||||
decoder.read_begin_hash
|
||||
rescue exc : ::CBOR::ParseError
|
||||
# TODO: Improve error message, use dedicated class
|
||||
raise "Error in mapping decoding when reading being hash: #{exc.message}"
|
||||
# raise ::JSON::MappingError.new(exc.message, self.class.to_s, nil, *%location, exc)
|
||||
raise ::CBOR::SerializationError.new(exc.message, self.class.to_s, nil)
|
||||
end
|
||||
|
||||
decoder.consume_hash do
|
||||
|
@ -204,9 +199,7 @@ module CBOR
|
|||
|
||||
{% if value[:nilable] || value[:has_default] %} } {% end %}
|
||||
rescue exc : ::CBOR::ParseError
|
||||
# TODO: Improve error message, use dedicated class
|
||||
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)
|
||||
raise ::CBOR::SerializationError.new(exc.message, self.class.to_s, {{value[:key]}})
|
||||
end
|
||||
{% end %}
|
||||
else
|
||||
|
@ -217,9 +210,7 @@ module CBOR
|
|||
{% for name, value in properties %}
|
||||
{% unless value[:nilable] || value[:has_default] %}
|
||||
if %var{name}.nil? && !%found{name} && !::Union({{value[:type]}}).nilable?
|
||||
# TODO: Improve error message, use dedicated class
|
||||
raise "Missing CBOR attribute"
|
||||
# raise ::JSON::MappingError.new("Missing JSON attribute: {{value[:key].id}}", self.class.to_s, nil, *%location, nil)
|
||||
raise ::CBOR::SerializationError.new("Missing CBOR attribute: {{value[:key].id}}", self.class.to_s, nil)
|
||||
end
|
||||
{% end %}
|
||||
|
||||
|
@ -247,7 +238,7 @@ module CBOR
|
|||
end
|
||||
|
||||
protected def on_unknown_cbor_attribute(decoder, key)
|
||||
decoder.skip
|
||||
raise ::CBOR::SerializationError.new("Unknown CBOR attribute: #{key}", self.class.to_s, nil)
|
||||
end
|
||||
|
||||
# protected def on_to_cbor(cbor : ::CBOR::Builder)
|
||||
|
@ -322,33 +313,25 @@ module CBOR
|
|||
# {% 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)
|
||||
# TODO: Improve error
|
||||
raise "Unknown CBOR attribute: #{key}"
|
||||
# raise ::JSON::MappingError.new("Unknown JSON attribute: #{key}", self.class.to_s, nil, *key_location, nil)
|
||||
cbor_unmapped[key] = begin
|
||||
decoder.read_value
|
||||
rescue exc : ::CBOR::ParseError
|
||||
raise ::CBOR::SerializationError.new(exc.message, self.class.to_s, key)
|
||||
end
|
||||
end
|
||||
|
||||
# protected def on_to_json(json)
|
||||
# json_unmapped.each do |key, value|
|
||||
# json.field(key) { value.to_json(json) }
|
||||
# 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.
|
||||
#
|
||||
# - *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
|
||||
|
||||
def to_t : Bool | Nil
|
||||
case self
|
||||
when False
|
||||
false
|
||||
when True
|
||||
true
|
||||
when Null,
|
||||
Undefined
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def is_nil? : Bool
|
||||
case self
|
||||
when Null, Undefined
|
||||
|
|
Loading…
Reference in New Issue