mirror of
https://github.com/logos-storage/nim-ethers.git
synced 2026-01-10 09:33:05 +00:00
Add more tests for OptIn/OptOut/Strict modes, fix logic
This commit is contained in:
parent
d8bf5b0eae
commit
4a5028f295
@ -5,7 +5,6 @@ import std/options
|
||||
import std/sequtils
|
||||
import std/sets
|
||||
import std/strutils
|
||||
# import std/strformat
|
||||
import std/tables
|
||||
import std/typetraits
|
||||
import pkg/chronicles except toJson
|
||||
@ -29,29 +28,33 @@ logScope:
|
||||
type
|
||||
SerdeError* = object of EthersError
|
||||
UnexpectedKindError* = object of SerdeError
|
||||
DeserializeMode* = enum
|
||||
Default, ## objects can have more or less fields than json
|
||||
OptIn, ## json must have fields marked with {.serialize.}
|
||||
Strict ## object fields and json fields must match exactly
|
||||
SerdeMode* = enum
|
||||
OptOut, ## serialize: all object fields will be serialized, except fields marked with 'ignore'
|
||||
## deserialize: all json keys will be deserialized, no error if extra json field
|
||||
OptIn, ## serialize: only object fields marked with serialize will be serialzied
|
||||
## deserialize: only fields marked with deserialize will be deserialized
|
||||
Strict ## serialize: all object fields will be serialized, regardless if the field is marked with 'ignore'
|
||||
## deserialize: object fields and json fields must match exactly
|
||||
SerdeFieldOptions = object
|
||||
key: string
|
||||
ignore: bool
|
||||
|
||||
# template serializeAll* {.pragma.}
|
||||
template serialize*(key = "", ignore = false) {.pragma.}
|
||||
template deserialize*(key = "", mode = DeserializeMode.Default) {.pragma.}
|
||||
template serialize*(key = "", ignore = false, mode = SerdeMode.OptOut) {.pragma.}
|
||||
template deserialize*(key = "", ignore = false, mode = SerdeMode.OptOut) {.pragma.}
|
||||
|
||||
template expectEmptyPragma(value, pragma, msg) =
|
||||
static:
|
||||
when value.hasCustomPragma(pragma):
|
||||
const params = value.getCustomPragmaVal(pragma)
|
||||
for param in params.fields:
|
||||
if param != typeof(param).default:
|
||||
raiseAssert(msg)
|
||||
proc isDefault[T](paramValue: T): bool {.compileTime.} =
|
||||
var result = paramValue == T.default
|
||||
when T is SerdeMode:
|
||||
return paramValue == SerdeMode.OptOut
|
||||
return result
|
||||
|
||||
template expectMissingPragmaParam(value, pragma, name, msg) =
|
||||
static:
|
||||
when value.hasCustomPragma(pragma):
|
||||
const params = value.getCustomPragmaVal(pragma)
|
||||
for paramName, paramValue in params.fieldPairs:
|
||||
if paramName == name and paramValue != typeof(paramValue).default:
|
||||
|
||||
if paramName == name and not paramValue.isDefault:
|
||||
raiseAssert(msg)
|
||||
|
||||
proc mapErrTo[E1: ref CatchableError, E2: SerdeError](
|
||||
@ -113,6 +116,49 @@ func keysNotIn[T](json: JsonNode, obj: T): HashSet[string] =
|
||||
let objKeys = obj.fieldKeys.toHashSet
|
||||
difference(jsonKeys, objKeys)
|
||||
|
||||
template getSerdeFieldOptions(pragma, fieldName, fieldValue): SerdeFieldOptions =
|
||||
var opts = SerdeFieldOptions(key: fieldName, ignore: false)
|
||||
when fieldValue.hasCustomPragma(pragma):
|
||||
fieldValue.expectMissingPragmaParam(pragma, "mode",
|
||||
"Cannot set " & astToStr(pragma) & " 'mode' on '" & fieldName & "' field defintion.")
|
||||
let (key, ignore, _) = fieldValue.getCustomPragmaVal(pragma)
|
||||
opts.ignore = ignore
|
||||
if key != "":
|
||||
opts.key = key
|
||||
opts
|
||||
|
||||
template getSerdeMode(T, pragma): SerdeMode =
|
||||
when T.hasCustomPragma(pragma):
|
||||
T.expectMissingPragmaParam(pragma, "key",
|
||||
"Cannot set " & astToStr(pragma) & " 'key' on '" & $T &
|
||||
"' type definition.")
|
||||
T.expectMissingPragmaParam(pragma, "ignore",
|
||||
"Cannot set " & astToStr(pragma) & " 'ignore' on '" & $T &
|
||||
"' type definition.")
|
||||
let (_, _, mode) = T.getCustomPragmaVal(pragma)
|
||||
mode
|
||||
else:
|
||||
# Default mode -- when the type is NOT annotated with a
|
||||
# serialize/deserialize pragma.
|
||||
#
|
||||
# NOTE This may be different in the logic branch above, when the type is
|
||||
# annotated with serialize/deserialize but doesn't specify a mode. The
|
||||
# default in that case will fallback to the default mode specified in the
|
||||
# pragma signature (currently OptOut for both serialize and deserialize)
|
||||
#
|
||||
# Examples:
|
||||
# 1. type MyObj = object
|
||||
# Type is not annotated, mode defaults to OptOut (as specified on the
|
||||
# pragma signatures) for both serialization and deserializtion
|
||||
#
|
||||
# 2. type MyObj {.serialize, deserialize.} = object
|
||||
# Type is annotated, mode defaults to OptIn for serialization and OptOut
|
||||
# for deserialization
|
||||
when pragma == serialize:
|
||||
SerdeMode.OptIn
|
||||
elif pragma == deserialize:
|
||||
SerdeMode.OptOut
|
||||
|
||||
proc fromJson*(
|
||||
T: type enum,
|
||||
json: JsonNode
|
||||
@ -245,31 +291,6 @@ proc fromJson*[T](
|
||||
arr.add(? T.fromJson(elem))
|
||||
success arr
|
||||
|
||||
template getSerializationKey(fieldName, fieldValue): string =
|
||||
when fieldValue.hasCustomPragma(serialize):
|
||||
let (key, _) = fieldValue.getCustomPragmaVal(serialize)
|
||||
if key != "": key
|
||||
else: fieldName
|
||||
else: fieldName
|
||||
|
||||
template getDeserializationKey(fieldName, fieldValue): string =
|
||||
let serializationKey = getSerializationKey(fieldName, fieldValue)
|
||||
when fieldValue.hasCustomPragma(deserialize):
|
||||
fieldValue.expectMissingPragmaParam(deserialize, "mode",
|
||||
"Cannot set 'mode' on field defintion.")
|
||||
let (key, mode) = fieldValue.getCustomPragmaVal(deserialize)
|
||||
if key != "": key
|
||||
else: serializationKey # defaults to fieldName
|
||||
else: serializationKey # defaults to fieldName
|
||||
|
||||
template getDeserializationMode(T): DeserializeMode =
|
||||
when T.hasCustomPragma(deserialize):
|
||||
T.expectMissingPragmaParam(deserialize, "key",
|
||||
"Cannot set 'key' on object definition.")
|
||||
T.getCustomPragmaVal(deserialize)[1] # mode = second pragma param
|
||||
else:
|
||||
DeserializeMode.Default
|
||||
|
||||
proc fromJson*[T: ref object or object](
|
||||
_: type T,
|
||||
json: JsonNode
|
||||
@ -280,48 +301,75 @@ proc fromJson*[T: ref object or object](
|
||||
|
||||
expectJsonKind(T, JObject, json)
|
||||
var res = when type(T) is ref: T.new() else: T.default
|
||||
let mode = T.getDeserializationMode()
|
||||
let mode = T.getSerdeMode(deserialize)
|
||||
|
||||
# ensure there's no extra fields in json
|
||||
if mode == SerdeMode.Strict:
|
||||
let extraFields = json.keysNotIn(res)
|
||||
if extraFields.len > 0:
|
||||
return failure newSerdeError("json field(s) missing in object: " & $extraFields)
|
||||
|
||||
for name, value in fieldPairs(when type(T) is ref: res[] else: res):
|
||||
|
||||
logScope:
|
||||
field = $T & "." & name
|
||||
mode
|
||||
|
||||
let key = getDeserializationKey(name, value)
|
||||
let hasDeserializePragma = value.hasCustomPragma(deserialize)
|
||||
let opts = getSerdeFieldOptions(deserialize, name, value)
|
||||
let isOptionalValue = typeof(value) is Option
|
||||
var skip = false # workaround for 'continue' not supported in a 'fields' loop
|
||||
|
||||
if mode == Strict and key notin json:
|
||||
return failure newSerdeError("object field missing in json: " & key)
|
||||
case mode:
|
||||
of Strict:
|
||||
if opts.key notin json:
|
||||
return failure newSerdeError("object field missing in json: " & opts.key)
|
||||
elif opts.ignore:
|
||||
# unable to figure out a way to make this a compile time check
|
||||
warn "object field marked as 'ignore' while in Strict mode, field will be deserialized anyway"
|
||||
|
||||
if mode == OptIn:
|
||||
if not value.hasCustomPragma(deserialize):
|
||||
debug "object field not marked as 'deserialize', skipping", name = name
|
||||
# use skip as workaround for 'continue' not supported in a 'fields' loop
|
||||
of OptIn:
|
||||
if not hasDeserializePragma:
|
||||
debug "object field not marked as 'deserialize', skipping"
|
||||
skip = true
|
||||
elif key notin json:
|
||||
return failure newSerdeError("object field missing in json: " & key)
|
||||
elif opts.ignore:
|
||||
debug "object field marked as 'ignore', skipping"
|
||||
skip = true
|
||||
elif opts.key notin json and not isOptionalValue:
|
||||
return failure newSerdeError("object field missing in json: " & opts.key)
|
||||
|
||||
if key in json and
|
||||
jsonVal =? json{key}.catch and
|
||||
not jsonVal.isNil and
|
||||
not skip:
|
||||
of OptOut:
|
||||
if opts.ignore:
|
||||
debug "object field is opted out of deserialization ('igore' is set), skipping"
|
||||
skip = true
|
||||
elif hasDeserializePragma and opts.key == name:
|
||||
warn "object field marked as deserialize in OptOut mode, but 'ignore' not set, field will be deserialized"
|
||||
|
||||
without parsed =? type(value).fromJson(jsonVal), e:
|
||||
warn "failed to deserialize field",
|
||||
`type` = $typeof(value),
|
||||
json = jsonVal,
|
||||
error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
if not skip:
|
||||
|
||||
elif mode == DeserializeMode.Default:
|
||||
debug "object field missing in json, skipping", key, json
|
||||
if isOptionalValue:
|
||||
|
||||
# ensure there's no extra fields in json
|
||||
if mode == DeserializeMode.Strict:
|
||||
let extraFields = json.keysNotIn(res)
|
||||
if extraFields.len > 0:
|
||||
return failure newSerdeError("json field(s) missing in object: " & $extraFields)
|
||||
let jsonVal = json{opts.key}
|
||||
without parsed =? typeof(value).fromJson(jsonVal), e:
|
||||
debug "failed to deserialize field",
|
||||
`type` = $typeof(value),
|
||||
json = jsonVal,
|
||||
error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
|
||||
# not Option[T]
|
||||
elif opts.key in json and
|
||||
jsonVal =? json{opts.key}.catch and
|
||||
not jsonVal.isNil:
|
||||
|
||||
without parsed =? typeof(value).fromJson(jsonVal), e:
|
||||
debug "failed to deserialize field",
|
||||
`type` = $typeof(value),
|
||||
json = jsonVal,
|
||||
error = e.msg
|
||||
return failure(e)
|
||||
value = parsed
|
||||
|
||||
success(res)
|
||||
|
||||
@ -395,32 +443,46 @@ func `%`*[T](opt: Option[T]): JsonNode =
|
||||
if opt.isSome: %(opt.get) else: newJNull()
|
||||
|
||||
|
||||
func `%`*[T: object or ref object](obj: T): JsonNode =
|
||||
|
||||
# T.expectMissingPragma(serialize, "Invalid pragma on object definition.")
|
||||
proc `%`*[T: object or ref object](obj: T): JsonNode =
|
||||
|
||||
let jsonObj = newJObject()
|
||||
let o = when T is ref object: obj[]
|
||||
else: obj
|
||||
|
||||
T.expectEmptyPragma(serialize, "Cannot specify 'key' or 'ignore' on object defition")
|
||||
|
||||
const serializeAllFields = T.hasCustomPragma(serialize)
|
||||
let mode = T.getSerdeMode(serialize)
|
||||
|
||||
for name, value in o.fieldPairs:
|
||||
# TODO: move to %
|
||||
# value.expectMissingPragma(deserializeMode, "Invalid pragma on field definition.")
|
||||
# static:
|
||||
const serializeField = value.hasCustomPragma(serialize)
|
||||
when serializeField:
|
||||
let (keyOverride, ignore) = value.getCustomPragmaVal(serialize)
|
||||
if not ignore:
|
||||
let key = if keyOverride != "": keyOverride
|
||||
else: name
|
||||
jsonObj[key] = %value
|
||||
|
||||
elif serializeAllFields:
|
||||
jsonObj[name] = %value
|
||||
logScope:
|
||||
field = $T & "." & name
|
||||
mode
|
||||
|
||||
let opts = getSerdeFieldOptions(serialize, name, value)
|
||||
const serializeField = value.hasCustomPragma(serialize)
|
||||
var skip = false # workaround for 'continue' not supported in a 'fields' loop
|
||||
|
||||
case mode:
|
||||
of OptIn:
|
||||
if not serializeField:
|
||||
debug "object field not marked with serialize, skipping"
|
||||
skip = true
|
||||
elif opts.ignore:
|
||||
skip = true
|
||||
|
||||
of OptOut:
|
||||
if opts.ignore:
|
||||
debug "object field opted out of serialization ('ignore' is set), skipping"
|
||||
skip = true
|
||||
elif serializeField and opts.key == name: # all serialize params are default
|
||||
warn "object field marked as serialize in OptOut mode, but 'ignore' not set, field will be serialized"
|
||||
|
||||
of Strict:
|
||||
if opts.ignore:
|
||||
# unable to figure out a way to make this a compile time check
|
||||
warn "object field marked as 'ignore' while in Strict mode, field will be serialized anyway"
|
||||
|
||||
if not skip:
|
||||
jsonObj[opts.key] = %value
|
||||
|
||||
jsonObj
|
||||
|
||||
@ -441,7 +503,7 @@ func `%`*[T: distinct](id: T): JsonNode =
|
||||
type baseType = T.distinctBase
|
||||
% baseType(id)
|
||||
|
||||
func toJson*[T](item: T): string = $(%item)
|
||||
proc toJson*[T](item: T): string = $(%item)
|
||||
|
||||
proc toJsnImpl(x: NimNode): NimNode =
|
||||
case x.kind
|
||||
|
||||
@ -228,6 +228,7 @@ suite "json serialization - deserialize":
|
||||
check deserialized.mystring == expected.mystring
|
||||
check deserialized.myint == expected.myint
|
||||
|
||||
|
||||
suite "json serialization pragmas":
|
||||
|
||||
test "fails to compile when object marked with 'serialize' specifies options":
|
||||
@ -284,12 +285,37 @@ suite "json serialization pragmas":
|
||||
|
||||
check compiles(%MyObj())
|
||||
|
||||
|
||||
suite "json serialization, mode = OptIn":
|
||||
|
||||
test "serializes with default mode OptIn when object not marked with serialize":
|
||||
type MyObj = object
|
||||
field1 {.serialize.}: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field1":true}"""
|
||||
|
||||
test "not marking object with serialize is equivalent to marking it with serialize in OptIn mode":
|
||||
type MyObj = object
|
||||
field1 {.serialize.}: bool
|
||||
field2: bool
|
||||
|
||||
type MyObjMarked {.serialize(mode=OptIn).} = object
|
||||
field1 {.serialize.}: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
let objMarked = MyObjMarked(field1: true, field2: true)
|
||||
check obj.toJson == objMarked.toJson
|
||||
|
||||
test "serializes field with key when specified":
|
||||
type MyObj = object
|
||||
field {.serialize("test").}: bool
|
||||
field1 {.serialize("test").}: bool
|
||||
field2 {.serialize.}: bool
|
||||
|
||||
let obj = MyObj(field: true)
|
||||
check obj.toJson == """{"test":true}"""
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"test":true,"field2":true}"""
|
||||
|
||||
test "does not serialize ignored field":
|
||||
type MyObj = object
|
||||
@ -299,7 +325,29 @@ suite "json serialization pragmas":
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field1":true}"""
|
||||
|
||||
test "serialize on object definition serializes all fields":
|
||||
|
||||
suite "json deserialization, mode = OptIn":
|
||||
|
||||
test "deserializes only fields marked as deserialize when mode is OptIn":
|
||||
type MyObj {.deserialize(mode=OptIn).} = object
|
||||
field1: int
|
||||
field2 {.deserialize.}: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
check val == MyObj(field1: 0, field2: true)
|
||||
|
||||
test "deserializes Optional fields when mode is OptIn":
|
||||
type MyObj {.deserialize(mode=OptIn).} = object
|
||||
field1 {.deserialize.}: bool
|
||||
field2 {.deserialize.}: Option[bool]
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true}""")
|
||||
check val == MyObj(field1: true, field2: none bool)
|
||||
|
||||
|
||||
suite "json serialization, mode = OptOut":
|
||||
|
||||
test "serialize on object definition defaults to OptOut mode, serializes all fields":
|
||||
type MyObj {.serialize.} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
@ -307,7 +355,20 @@ suite "json serialization pragmas":
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field1":true,"field2":true}"""
|
||||
|
||||
test "ignores field when object has serialize":
|
||||
test "not specifying serialize mode is equivalent to specifying OptOut mode":
|
||||
type MyObj {.serialize.} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
type MyObjMarked {.serialize(mode=OptOut).} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
let objMarked = MyObjMarked(field1: true, field2: true)
|
||||
check obj.toJson == objMarked.toJson
|
||||
|
||||
test "ignores field when marked with ignore":
|
||||
type MyObj {.serialize.} = object
|
||||
field1 {.serialize(ignore=true).}: bool
|
||||
field2: bool
|
||||
@ -315,7 +376,7 @@ suite "json serialization pragmas":
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field2":true}"""
|
||||
|
||||
test "serializes field with key when object has serialize":
|
||||
test "serializes field with key instead of field name":
|
||||
type MyObj {.serialize.} = object
|
||||
field1 {.serialize("test").}: bool
|
||||
field2: bool
|
||||
@ -323,6 +384,81 @@ suite "json serialization pragmas":
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"test":true,"field2":true}"""
|
||||
|
||||
|
||||
suite "json deserialization, mode = OptOut":
|
||||
|
||||
test "deserializes object in OptOut mode when not marked with deserialize":
|
||||
type MyObj = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field3":true}""")
|
||||
check val == MyObj(field1: true, field2: false)
|
||||
|
||||
test "deserializes object field with marked json key":
|
||||
type MyObj = object
|
||||
field1 {.deserialize("test").}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"test":true,"field2":true}""")
|
||||
check val == MyObj(field1: true, field2: true)
|
||||
|
||||
test "fails to deserialize object field with wrong type":
|
||||
type MyObj = object
|
||||
field1: int
|
||||
field2: bool
|
||||
|
||||
let r = MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
check r.isFailure
|
||||
check r.error of UnexpectedKindError
|
||||
check r.error.msg == "deserialization to int failed: expected {JInt} but got JBool"
|
||||
|
||||
test "does not deserialize ignored fields in OptOut mode":
|
||||
type MyObj = object
|
||||
field1 {.deserialize(ignore=true).}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
check val == MyObj(field1: false, field2: true)
|
||||
|
||||
test "deserializes fields when marked with deserialize but not ignored":
|
||||
type MyObj = object
|
||||
field1 {.deserialize.}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
check val == MyObj(field1: true, field2: true)
|
||||
|
||||
test "deserializes Optional field":
|
||||
type MyObj = object
|
||||
field1: Option[bool]
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field2":true}""")
|
||||
check val == MyObj(field1: none bool, field2: true)
|
||||
|
||||
|
||||
suite "json serialization - mode = Strict":
|
||||
|
||||
test "serializes all fields in Strict mode":
|
||||
type MyObj {.serialize(mode=Strict).} = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field1":true,"field2":true}"""
|
||||
|
||||
test "ignores ignored fields in Strict mode":
|
||||
type MyObj {.serialize(mode=Strict).} = object
|
||||
field1 {.serialize(ignore=true).}: bool
|
||||
field2: bool
|
||||
|
||||
let obj = MyObj(field1: true, field2: true)
|
||||
check obj.toJson == """{"field1":true,"field2":true}"""
|
||||
|
||||
|
||||
suite "json deserialization, mode = Strict":
|
||||
|
||||
test "deserializes matching object and json fields when mode is Strict":
|
||||
type MyObj {.deserialize(mode=Strict).} = object
|
||||
field1: bool
|
||||
@ -350,52 +486,13 @@ suite "json serialization pragmas":
|
||||
check r.error of SerdeError
|
||||
check r.error.msg == "json field(s) missing in object: {\"field1\"}"
|
||||
|
||||
test "deserializes only fields marked as deserialize when mode is OptIn":
|
||||
type MyObj {.deserialize(mode=OptIn).} = object
|
||||
field1: int
|
||||
field2 {.deserialize.}: bool
|
||||
test "deserializes ignored fields in Strict mode":
|
||||
type MyObj {.deserialize(mode=Strict).} = object
|
||||
field1 {.deserialize(ignore=true).}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
check val == MyObj(field1: 0, field2: true)
|
||||
|
||||
test "can deserialize object in default mode when not marked with deserialize":
|
||||
type MyObj = object
|
||||
field1: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"field1":true,"field3":true}""")
|
||||
check val == MyObj(field1: true, field2: false)
|
||||
|
||||
test "deserializes object field with marked json key":
|
||||
type MyObj = object
|
||||
field1 {.deserialize("test").}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"test":true,"field2":true}""")
|
||||
check val == MyObj(field1: true, field2: true)
|
||||
|
||||
test "deserialization key can be set using serialize key":
|
||||
type MyObj = object
|
||||
field1 {.serialize("test").}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"test":true,"field2":true}""")
|
||||
check val == MyObj(field1: true, field2: true)
|
||||
|
||||
test "deserialization key takes priority over serialize key":
|
||||
type MyObj = object
|
||||
field1 {.serialize("test"), deserialize("test1").}: bool
|
||||
field2: bool
|
||||
|
||||
let val = !MyObj.fromJson("""{"test":false,"test1":true,"field2":true}""")
|
||||
check val == MyObj(field1: true, field2: true)
|
||||
|
||||
test "fails to deserialize object field with wrong type":
|
||||
type MyObj = object
|
||||
field1: int
|
||||
field2: bool
|
||||
|
||||
let r = MyObj.fromJson("""{"field1":true,"field2":true}""")
|
||||
check r.isFailure
|
||||
check r.error of UnexpectedKindError
|
||||
check r.error.msg == "deserialization to int failed: expected {JInt} but got JBool"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user