2023-12-13 09:07:57 +00:00
|
|
|
# json-serialization
|
2024-02-29 06:58:51 +00:00
|
|
|
# Copyright (c) 2019-2024 Status Research & Development GmbH
|
2023-12-13 09:07:57 +00:00
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
|
|
# at your option.
|
|
|
|
# This file may not be copied, modified, or distributed except according to
|
|
|
|
# those terms.
|
|
|
|
|
2019-03-19 23:54:03 +00:00
|
|
|
import
|
2023-12-18 04:05:12 +00:00
|
|
|
std/tables,
|
2019-03-19 23:54:03 +00:00
|
|
|
serialization/errors
|
|
|
|
|
|
|
|
export
|
2023-12-18 04:05:12 +00:00
|
|
|
tables,
|
2019-03-19 23:54:03 +00:00
|
|
|
errors
|
|
|
|
|
2019-01-21 17:40:14 +00:00
|
|
|
type
|
2019-03-19 23:54:03 +00:00
|
|
|
JsonError* = object of SerializationError
|
2019-01-21 17:40:14 +00:00
|
|
|
|
2023-12-18 04:05:12 +00:00
|
|
|
# This is a special type to parse whatever
|
|
|
|
# json value into string.
|
2020-06-01 18:15:22 +00:00
|
|
|
JsonString* = distinct string
|
|
|
|
|
2023-12-18 04:05:12 +00:00
|
|
|
# This is a special type to parse whatever
|
|
|
|
# json value into nothing/skip it.
|
|
|
|
JsonVoid* = object
|
|
|
|
|
|
|
|
JsonSign* {.pure.} = enum
|
|
|
|
None
|
|
|
|
Pos
|
|
|
|
Neg
|
|
|
|
|
|
|
|
# This is a special type to parse complete
|
|
|
|
# json number.
|
|
|
|
JsonNumber*[T: string or uint64] = object
|
|
|
|
sign*: JsonSign
|
|
|
|
integer*: T
|
|
|
|
fraction*: string
|
|
|
|
expSign*: JsonSign
|
|
|
|
exponent*: T
|
|
|
|
|
|
|
|
JsonReaderFlag* {.pure.} = enum
|
|
|
|
allowUnknownFields
|
|
|
|
requireAllFields
|
|
|
|
escapeHex
|
|
|
|
relaxedEscape
|
|
|
|
portableInt
|
|
|
|
trailingComma # on
|
|
|
|
allowComments # on
|
|
|
|
leadingFraction # on
|
|
|
|
integerPositiveSign # on
|
|
|
|
|
|
|
|
JsonReaderFlags* = set[JsonReaderFlag]
|
|
|
|
|
|
|
|
JsonReaderConf* = object
|
|
|
|
nestedDepthLimit*: int
|
|
|
|
arrayElementsLimit*: int
|
|
|
|
objectMembersLimit*: int
|
|
|
|
integerDigitsLimit*: int
|
|
|
|
fractionDigitsLimit*: int
|
|
|
|
exponentDigitsLimit*: int
|
|
|
|
stringLengthLimit*: int
|
|
|
|
|
|
|
|
JsonValueKind* {.pure.} = enum
|
|
|
|
String,
|
|
|
|
Number,
|
|
|
|
Object,
|
|
|
|
Array,
|
|
|
|
Bool,
|
|
|
|
Null
|
|
|
|
|
|
|
|
JsonObjectType*[T: string or uint64] = OrderedTable[string, JsonValueRef[T]]
|
|
|
|
|
|
|
|
JsonValueRef*[T: string or uint64] = ref JsonValue[T]
|
|
|
|
JsonValue*[T: string or uint64] = object
|
|
|
|
case kind*: JsonValueKind
|
|
|
|
of JsonValueKind.String:
|
|
|
|
strVal*: string
|
|
|
|
of JsonValueKind.Number:
|
|
|
|
numVal*: JsonNumber[T]
|
|
|
|
of JsonValueKind.Object:
|
|
|
|
objVal*: JsonObjectType[T]
|
|
|
|
of JsonValueKind.Array:
|
|
|
|
arrayVal*: seq[JsonValueRef[T]]
|
|
|
|
of JsonValueKind.Bool:
|
|
|
|
boolVal*: bool
|
|
|
|
of JsonValueKind.Null:
|
|
|
|
discard
|
|
|
|
|
|
|
|
|
2019-01-21 17:40:14 +00:00
|
|
|
const
|
|
|
|
minPortableInt* = -9007199254740991 # -2**53 + 1
|
|
|
|
maxPortableInt* = 9007199254740991 # +2**53 - 1
|
|
|
|
|
2023-12-18 04:05:12 +00:00
|
|
|
defaultJsonReaderFlags* = {
|
|
|
|
JsonReaderFlag.integerPositiveSign,
|
|
|
|
JsonReaderFlag.allowComments,
|
|
|
|
JsonReaderFlag.leadingFraction,
|
|
|
|
JsonReaderFlag.trailingComma,
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultJsonReaderConf* = JsonReaderConf(
|
|
|
|
nestedDepthLimit: 512,
|
|
|
|
arrayElementsLimit: 0,
|
|
|
|
objectMembersLimit: 0,
|
|
|
|
integerDigitsLimit: 128,
|
|
|
|
fractionDigitsLimit: 128,
|
|
|
|
exponentDigitsLimit: 32,
|
|
|
|
stringLengthLimit: 0,
|
|
|
|
)
|
|
|
|
|
|
|
|
{.push gcsafe, raises: [].}
|
|
|
|
|
2020-07-17 20:58:02 +00:00
|
|
|
template `==`*(lhs, rhs: JsonString): bool =
|
|
|
|
string(lhs) == string(rhs)
|
2023-12-18 04:05:12 +00:00
|
|
|
|
|
|
|
template valueType*[K, V](_: type OrderedTable[K, V]): untyped = V
|
|
|
|
|
|
|
|
func isFloat*(x: JsonNumber): bool =
|
|
|
|
x.fraction.len > 0
|
|
|
|
|
|
|
|
func hasExponent*[T](x: JsonNumber[T]): bool =
|
|
|
|
when T is string:
|
|
|
|
x.exponent.len > 0
|
|
|
|
else:
|
|
|
|
x.exponent > 0
|
|
|
|
|
|
|
|
func toInt*(sign: JsonSign): int =
|
|
|
|
case sign:
|
|
|
|
of JsonSign.None: 1
|
|
|
|
of JsonSign.Pos: 0
|
|
|
|
of JsonSign.Neg: -1
|
|
|
|
|
|
|
|
func `==`*(lhs, rhs: JsonValueRef): bool =
|
|
|
|
if lhs.isNil and rhs.isNil:
|
|
|
|
return true
|
|
|
|
|
2023-12-29 05:33:58 +00:00
|
|
|
if not lhs.isNil and rhs.isNil:
|
2023-12-18 04:05:12 +00:00
|
|
|
return false
|
|
|
|
|
|
|
|
if lhs.isNil and not rhs.isNil:
|
|
|
|
return false
|
|
|
|
|
|
|
|
if lhs.kind != rhs.kind:
|
|
|
|
return false
|
|
|
|
|
|
|
|
case lhs.kind
|
|
|
|
of JsonValueKind.String:
|
|
|
|
lhs.strVal == rhs.strVal
|
|
|
|
of JsonValueKind.Number:
|
|
|
|
lhs.numVal == rhs.numVal
|
|
|
|
of JsonValueKind.Object:
|
|
|
|
if lhs.objVal.len != rhs.objVal.len:
|
2024-02-29 06:58:51 +00:00
|
|
|
return false
|
2023-12-18 04:05:12 +00:00
|
|
|
for k, v in lhs.objVal:
|
|
|
|
let rhsVal = rhs.objVal.getOrDefault(k, nil)
|
|
|
|
if rhsVal.isNil:
|
|
|
|
return false
|
|
|
|
if rhsVal != v:
|
|
|
|
return false
|
|
|
|
true
|
|
|
|
of JsonValueKind.Array:
|
|
|
|
if lhs.arrayVal.len != rhs.arrayVal.len:
|
|
|
|
return false
|
|
|
|
for i, x in lhs.arrayVal:
|
|
|
|
if x != rhs.arrayVal[i]:
|
|
|
|
return false
|
|
|
|
true
|
|
|
|
of JsonValueKind.Bool:
|
|
|
|
lhs.boolVal == rhs.boolVal
|
|
|
|
of JsonValueKind.Null:
|
|
|
|
true
|
|
|
|
|
|
|
|
{.pop.}
|