Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions json_serialization/format.nim
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ template createJsonFlavor*(FlavorName: untyped,
requireAllFields = true,
omitOptionalFields = true,
allowUnknownFields = true,
skipNullFields = false) {.dirty.} =
skipNullFields = false,
automaticPrimitivesSerialization = true) {.dirty.} =
when declared(SerializationFormat): # Earlier versions lack mimeTypeValue
createFlavor(Json, FlavorName, mimeTypeValue)
else:
Expand Down Expand Up @@ -137,5 +138,4 @@ template createJsonFlavor*(FlavorName: untyped,
# Set default to true for backward compatibility
# but user can call it again later with different value.
# Or fine tuning use `Flavor.automaticSerialization(type, true/false)`
FlavorName.automaticBuiltinSerialization(true)

FlavorName.automaticBuiltinSerialization(automaticPrimitivesSerialization)
223 changes: 124 additions & 99 deletions json_serialization/reader_impl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -225,22 +225,66 @@ proc readRecordValue*[T](r: var JsonReader, value: var T)
else:
r.raiseUnexpectedField(key, cstring typeName)

template autoSerializeCheck(F: distinct type, T: distinct type) =
template autoSerializeCheck(F: distinct type, T: distinct type, body) =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this helper in n-j-s and not in nim-serialization?

when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey
mixin typeAutoSerialize
when not F.typeAutoSerialize(T):
const typeName = typetraits.name(T)
{.error: "automatic serialization is not enabled or readValue not implemented for `" &
const
typeName = typetraits.name(T)
flavorName = typetraits.name(F)
{.error: flavorName &
": automatic serialization is not enabled or readValue not implemented for `" &
typeName & "`".}
else:
body
else:
body

template autoSerializeCheck(F: distinct type, TC: distinct type, M: distinct type) =
template autoSerializeCheck(F: distinct type, TC: distinct type, M: distinct type, body) =
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey
mixin typeClassOrMemberAutoSerialize
when not F.typeClassOrMemberAutoSerialize(TC, M):
const typeName = typetraits.name(M)
const typeClassName = typetraits.name(TC)
{.error: "automatic serialization is not enabled or readValue not implemented for `" &
const
typeName = typetraits.name(M)
typeClassName = typetraits.name(TC)
flavorName = typetraits.name(F)
{.error: flavorName &
": automatic serialization is not enabled or readValue not implemented for `" &
typeName & "` of typeclass `" & typeClassName & "`".}
else:
body
else:
body

template readValueRefOrPtr(r, value) =
mixin readValue
when compiles(isNotNilCheck(value)):
allocPtr value
value[] = readValue(r, type(value[]))
else:
if r.tokKind == JsonValueKind.Null:
value = nil
r.parseNull()
else:
allocPtr value
value[] = readValue(r, type(value[]))

template readValueObjectOrTuple(Flavor, r, value) =
mixin flavorUsesAutomaticObjectSerialization

const isAutomatic =
flavorUsesAutomaticObjectSerialization(Flavor)

when not isAutomatic:
const
flavor =
"JsonReader[" & typetraits.name(typeof(r).Flavor) & "], " &
typetraits.name(T)
{.error:
"Missing Json serialization import or implementation for readValue(" &
flavor & ")".}

readRecordValue(r, value)

proc readValue*[T](r: var JsonReader, value: var T)
{.raises: [SerializationError, IOError].} =
Expand All @@ -261,129 +305,110 @@ proc readValue*[T](r: var JsonReader, value: var T)
type Flavor = JsonReader.Flavor

when value is JsonString:
autoSerializeCheck(Flavor, JsonString)
value = r.parseAsString()
autoSerializeCheck(Flavor, JsonString):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it feels like this check could be spelled as when autoSerializeCheck(...)

value = r.parseAsString()

elif value is JsonNode:
autoSerializeCheck(Flavor, JsonNode)
value = r.parseJsonNode()
autoSerializeCheck(Flavor, JsonNode):
value = r.parseJsonNode()

elif value is JsonNumber:
autoSerializeCheck(Flavor, JsonNumber)
r.parseNumber(value)
autoSerializeCheck(Flavor, JsonNumber):
r.parseNumber(value)

elif value is JsonVoid:
autoSerializeCheck(Flavor, JsonVoid)
r.skipSingleJsValue()
autoSerializeCheck(Flavor, JsonVoid):
r.skipSingleJsValue()

elif value is JsonValueRef:
autoSerializeCheck(Flavor, JsonValueRef)
r.parseValue(value)
autoSerializeCheck(Flavor, JsonValueRef):
r.parseValue(value)

elif value is string:
autoSerializeCheck(Flavor, string)
value = r.parseString()
autoSerializeCheck(Flavor, string):
value = r.parseString()

elif value is seq[char]:
autoSerializeCheck(Flavor, seq[char])
let val = r.parseString()
value.setLen(val.len)
for i in 0..<val.len:
value[i] = val[i]
autoSerializeCheck(Flavor, seq[char]):
let val = r.parseString()
value.setLen(val.len)
for i in 0..<val.len:
value[i] = val[i]

elif isCharArray(value):
autoSerializeCheck(Flavor, array, typeof(value))
let val = r.parseString()
if val.len != value.len:
# Raise tkString because we expected a `"` earlier
r.raiseUnexpectedToken(etString)
for i in 0..<value.len:
value[i] = val[i]
autoSerializeCheck(Flavor, array, typeof(value)):
let val = r.parseString()
if val.len != value.len:
# Raise tkString because we expected a `"` earlier
r.raiseUnexpectedToken(etString)
for i in 0..<value.len:
value[i] = val[i]

elif value is bool:
autoSerializeCheck(Flavor, bool)
value = r.parseBool()
autoSerializeCheck(Flavor, bool):
value = r.parseBool()

elif value is ref|ptr:
when value is ref:
autoSerializeCheck(Flavor, ref, typeof(value))
elif value is ref:
autoSerializeCheck(Flavor, ref, typeof(value)):
readValueRefOrPtr(r, value)

when value is ptr:
autoSerializeCheck(Flavor, ptr, typeof(value))

when compiles(isNotNilCheck(value)):
allocPtr value
value[] = readValue(r, type(value[]))
else:
if r.tokKind == JsonValueKind.Null:
value = nil
r.parseNull()
else:
allocPtr value
value[] = readValue(r, type(value[]))
elif value is ptr:
autoSerializeCheck(Flavor, ptr, typeof(value)):
readValueRefOrPtr(r, value)

elif value is enum:
autoSerializeCheck(Flavor, enum, typeof(value))
r.parseEnum(value)
autoSerializeCheck(Flavor, enum, typeof(value)):
r.parseEnum(value)

elif value is SomeInteger:
autoSerializeCheck(Flavor, SomeInteger, typeof(value))
value = r.parseInt(typeof value,
JsonReaderFlag.portableInt in r.lex.flags)
autoSerializeCheck(Flavor, SomeInteger, typeof(value)):
value = r.parseInt(typeof value,
JsonReaderFlag.portableInt in r.lex.flags)

elif value is SomeFloat:
autoSerializeCheck(Flavor, SomeFloat, typeof(value))
let val = r.parseNumber(uint64)
if val.isFloat:
value = r.toFloat(val, typeof value)
else:
value = T(val.integer)
autoSerializeCheck(Flavor, SomeFloat, typeof(value)):
let val = r.parseNumber(uint64)
if val.isFloat:
value = r.toFloat(val, typeof value)
else:
value = T(val.integer)

elif value is seq:
autoSerializeCheck(Flavor, seq, typeof(value))
r.parseArray:
let lastPos = value.len
value.setLen(lastPos + 1)
readValue(r, value[lastPos])
autoSerializeCheck(Flavor, seq, typeof(value)):
r.parseArray:
let lastPos = value.len
value.setLen(lastPos + 1)
readValue(r, value[lastPos])

elif value is array:
autoSerializeCheck(Flavor, array, typeof(value))
type IDX = typeof low(value)
r.parseArray(idx):
if idx < value.len:
let i = IDX(idx + low(value).int)
readValue(r, value[i])
else:
r.raiseUnexpectedValue("Too many items for " & $(typeof(value)))

elif value is (object or tuple):
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey
when value is object:
autoSerializeCheck(Flavor, object, typeof(value))

when value is tuple:
autoSerializeCheck(Flavor, tuple, typeof(value))

# Keep existing object/tuple auto serialization
# But make it deprecated.
mixin flavorUsesAutomaticObjectSerialization
autoSerializeCheck(Flavor, array, typeof(value)):
type IDX = typeof low(value)
r.parseArray(idx):
if idx < value.len:
let i = IDX(idx + low(value).int)
readValue(r, value[i])
else:
r.raiseUnexpectedValue("Too many items for " & $(typeof(value)))

elif value is object:
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey and cannot accept `object` param
autoSerializeCheck(Flavor, object, typeof(value)):
readValueObjectOrTuple(Flavor, r, value)
else:
readValueObjectOrTuple(Flavor, r, value)

const isAutomatic =
flavorUsesAutomaticObjectSerialization(Flavor)
elif value is tuple:
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey and cannot accept `tuple` param
autoSerializeCheck(Flavor, tuple, typeof(value)):
readValueObjectOrTuple(Flavor, r, value)
else:
readValueObjectOrTuple(Flavor, r, value)

when not isAutomatic:
const
flavor =
"JsonReader[" & typetraits.name(typeof(r).Flavor) & "], " &
typetraits.name(T)
{.error:
"Missing Json serialization import or implementation for readValue(" &
flavor & ")".}

readRecordValue(r, value)
else:
const typeName = typetraits.name(T)
{.error: "Failed to convert to JSON an unsupported type: " &
const
typeName = typetraits.name(T)
flavorName = typetraits.name(Flavor)
{.error: flavorName & ": Failed to convert from JSON an unsupported type: " &
typeName.}

iterator readObjectFields*(r: var JsonReader): string {.
Expand Down
Loading
Loading