Add JsonReader.init(..., requireAllFields: bool)
This commit is contained in:
parent
652099a959
commit
4f3775ddf4
|
@ -14,6 +14,7 @@ type
|
|||
JsonReader*[Flavor = DefaultFlavor] = object
|
||||
lexer*: JsonLexer
|
||||
allowUnknownFields: bool
|
||||
requireAllFields: bool
|
||||
|
||||
JsonReaderError* = object of JsonError
|
||||
line*, col*: int
|
||||
|
@ -46,6 +47,9 @@ type
|
|||
|
||||
UnexpectedValueError* = object of JsonReaderError
|
||||
|
||||
IncompleteObjectError* = object of JsonReaderError
|
||||
objectType: cstring
|
||||
|
||||
IntOverflowError* = object of JsonReaderError
|
||||
isNegative: bool
|
||||
absIntVal: uint64
|
||||
|
@ -79,6 +83,9 @@ method formatMsg*(err: ref IntOverflowError, filename: string): string =
|
|||
method formatMsg*(err: ref UnexpectedValueError, filename: string): string =
|
||||
tryFmt: fmt"{filename}({err.line}, {err.col}) {err.msg}"
|
||||
|
||||
method formatMsg*(err: ref IncompleteObjectError, filename: string): string =
|
||||
tryFmt: fmt"{filename}({err.line}, {err.col}) Not all required fields were specified when reading '{err.objectType}'"
|
||||
|
||||
proc assignLineNumber*(ex: ref JsonReaderError, r: JsonReader) =
|
||||
ex.line = r.lexer.line
|
||||
ex.col = r.lexer.tokenStartCol
|
||||
|
@ -111,6 +118,12 @@ proc raiseUnexpectedField*(r: JsonReader, fieldName, deserializedType: cstring)
|
|||
ex.deserializedType = deserializedType
|
||||
raise ex
|
||||
|
||||
proc raiseIncompleteObject*(r: JsonReader, objectType: cstring) {.noreturn.} =
|
||||
var ex = new IncompleteObjectError
|
||||
ex.assignLineNumber(r)
|
||||
ex.objectType = objectType
|
||||
raise ex
|
||||
|
||||
proc handleReadException*(r: JsonReader,
|
||||
Record: type,
|
||||
fieldName: string,
|
||||
|
@ -125,8 +138,10 @@ proc handleReadException*(r: JsonReader,
|
|||
proc init*(T: type JsonReader,
|
||||
stream: InputStream,
|
||||
mode = defaultJsonMode,
|
||||
allowUnknownFields = false): T =
|
||||
allowUnknownFields = false,
|
||||
requireAllFields = false): T =
|
||||
result.allowUnknownFields = allowUnknownFields
|
||||
result.requireAllFields = requireAllFields
|
||||
result.lexer = JsonLexer.init(stream, mode)
|
||||
result.lexer.next()
|
||||
|
||||
|
@ -500,7 +515,9 @@ proc readValue*[T](r: var JsonReader, value: var T)
|
|||
type T = type(value)
|
||||
r.skipToken tkCurlyLe
|
||||
|
||||
when T.totalSerializedFields > 0:
|
||||
const expectedFields = T.totalSerializedFields
|
||||
var readFields = 0
|
||||
when expectedFields > 0:
|
||||
let fields = T.fieldReadersTable(ReaderType)
|
||||
var expectedFieldPos = 0
|
||||
while r.lexer.tok == tkString:
|
||||
|
@ -513,6 +530,7 @@ proc readValue*[T](r: var JsonReader, value: var T)
|
|||
r.skipToken tkColon
|
||||
if reader != nil:
|
||||
reader(value, r)
|
||||
inc readFields
|
||||
elif r.allowUnknownFields:
|
||||
r.skipSingleJsValue()
|
||||
else:
|
||||
|
@ -523,6 +541,10 @@ proc readValue*[T](r: var JsonReader, value: var T)
|
|||
else:
|
||||
break
|
||||
|
||||
if r.requireAllFields and readFields != expectedFields:
|
||||
const typeName = typetraits.name(T)
|
||||
r.raiseIncompleteObject(typeName)
|
||||
|
||||
r.skipToken tkCurlyRi
|
||||
|
||||
else:
|
||||
|
|
|
@ -132,6 +132,85 @@ suite "toJson tests":
|
|||
let shouldNotDecode = Json.decode(json, Simple)
|
||||
echo "This should not have decoded ", shouldNotDecode
|
||||
|
||||
test "all fields are required and present":
|
||||
let json = dedent"""
|
||||
{
|
||||
"x": 20,
|
||||
"distance": 10,
|
||||
"y": "y value"
|
||||
}
|
||||
"""
|
||||
|
||||
let decoded = Json.decode(json, Simple, requireAllFields = true)
|
||||
|
||||
check:
|
||||
decoded.x == 20
|
||||
decoded.y == "y value"
|
||||
decoded.distance.int == 10
|
||||
|
||||
test "all fields were required, but not all were provided":
|
||||
let json = dedent"""
|
||||
{
|
||||
"x": -20,
|
||||
"distance": 10
|
||||
}
|
||||
"""
|
||||
|
||||
expect IncompleteObjectError:
|
||||
let shouldNotDecode = Json.decode(json, Simple, requireAllFields = true)
|
||||
echo "This should not have decoded ", shouldNotDecode
|
||||
|
||||
test "all fields were required, but not all were provided (additional fields present instead)":
|
||||
let json = dedent"""
|
||||
{
|
||||
"futureBool": false,
|
||||
"y": "y value",
|
||||
"futureObject": {"a": -1, "b": [1, 2.0, 3.1], "c": null, "d": true},
|
||||
"distance": 10
|
||||
}
|
||||
"""
|
||||
|
||||
expect IncompleteObjectError:
|
||||
let shouldNotDecode = Json.decode(json, Simple,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
echo "This should not have decoded ", shouldNotDecode
|
||||
|
||||
test "all fields were required, but none were provided":
|
||||
let json = "{}"
|
||||
|
||||
expect IncompleteObjectError:
|
||||
let shouldNotDecode = Json.decode(json, Simple, requireAllFields = true)
|
||||
echo "This should not have decoded ", shouldNotDecode
|
||||
|
||||
test "all fields are required and provided, and additional ones are present":
|
||||
let json = dedent"""
|
||||
{
|
||||
"x": 20,
|
||||
"distance": 10,
|
||||
"futureBool": false,
|
||||
"y": "y value",
|
||||
"futureObject": {"a": -1, "b": [1, 2.0, 3.1], "c": null, "d": true},
|
||||
}
|
||||
"""
|
||||
|
||||
let decoded = try:
|
||||
Json.decode(json, Simple, requireAllFields = true, allowUnknownFields = true)
|
||||
except SerializationError as err:
|
||||
checkpoint "Unexpected deserialization failure: " & err.formatMsg("<input>")
|
||||
raise
|
||||
|
||||
check:
|
||||
decoded.x == 20
|
||||
decoded.y == "y value"
|
||||
decoded.distance.int == 10
|
||||
|
||||
expect UnexpectedField:
|
||||
let shouldNotDecode = Json.decode(json, Simple,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = false)
|
||||
echo "This should not have decoded ", shouldNotDecode
|
||||
|
||||
test "arrays are printed correctly":
|
||||
var x = HoldsArray(data: @[1, 2, 3, 4])
|
||||
|
||||
|
|
Loading…
Reference in New Issue