Skip to content

Commit b08247e

Browse files
authored
Flavor automatic serialization rework (#124)
* Flavor automatic serialization rework Now the default handler is reduced to nothing if disabled. And it will be available when turned on in the caller module start from the callsite to all modules imported it. * fix * Fix for nim 1.6
1 parent c20d95a commit b08247e

File tree

3 files changed

+271
-206
lines changed

3 files changed

+271
-206
lines changed

json_serialization/format.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ template createJsonFlavor*(FlavorName: untyped,
106106
requireAllFields = true,
107107
omitOptionalFields = true,
108108
allowUnknownFields = true,
109-
skipNullFields = false) {.dirty.} =
109+
skipNullFields = false,
110+
automaticPrimitivesSerialization = true) {.dirty.} =
110111
when declared(SerializationFormat): # Earlier versions lack mimeTypeValue
111112
createFlavor(Json, FlavorName, mimeTypeValue)
112113
else:
@@ -137,5 +138,4 @@ template createJsonFlavor*(FlavorName: untyped,
137138
# Set default to true for backward compatibility
138139
# but user can call it again later with different value.
139140
# Or fine tuning use `Flavor.automaticSerialization(type, true/false)`
140-
FlavorName.automaticBuiltinSerialization(true)
141-
141+
FlavorName.automaticBuiltinSerialization(automaticPrimitivesSerialization)

json_serialization/reader_impl.nim

Lines changed: 124 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -225,22 +225,66 @@ proc readRecordValue*[T](r: var JsonReader, value: var T)
225225
else:
226226
r.raiseUnexpectedField(key, cstring typeName)
227227

228-
template autoSerializeCheck(F: distinct type, T: distinct type) =
228+
template autoSerializeCheck(F: distinct type, T: distinct type, body) =
229229
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey
230230
mixin typeAutoSerialize
231231
when not F.typeAutoSerialize(T):
232-
const typeName = typetraits.name(T)
233-
{.error: "automatic serialization is not enabled or readValue not implemented for `" &
232+
const
233+
typeName = typetraits.name(T)
234+
flavorName = typetraits.name(F)
235+
{.error: flavorName &
236+
": automatic serialization is not enabled or readValue not implemented for `" &
234237
typeName & "`".}
238+
else:
239+
body
240+
else:
241+
body
235242

236-
template autoSerializeCheck(F: distinct type, TC: distinct type, M: distinct type) =
243+
template autoSerializeCheck(F: distinct type, TC: distinct type, M: distinct type, body) =
237244
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey
238245
mixin typeClassOrMemberAutoSerialize
239246
when not F.typeClassOrMemberAutoSerialize(TC, M):
240-
const typeName = typetraits.name(M)
241-
const typeClassName = typetraits.name(TC)
242-
{.error: "automatic serialization is not enabled or readValue not implemented for `" &
247+
const
248+
typeName = typetraits.name(M)
249+
typeClassName = typetraits.name(TC)
250+
flavorName = typetraits.name(F)
251+
{.error: flavorName &
252+
": automatic serialization is not enabled or readValue not implemented for `" &
243253
typeName & "` of typeclass `" & typeClassName & "`".}
254+
else:
255+
body
256+
else:
257+
body
258+
259+
template readValueRefOrPtr(r, value) =
260+
mixin readValue
261+
when compiles(isNotNilCheck(value)):
262+
allocPtr value
263+
value[] = readValue(r, type(value[]))
264+
else:
265+
if r.tokKind == JsonValueKind.Null:
266+
value = nil
267+
r.parseNull()
268+
else:
269+
allocPtr value
270+
value[] = readValue(r, type(value[]))
271+
272+
template readValueObjectOrTuple(Flavor, r, value) =
273+
mixin flavorUsesAutomaticObjectSerialization
274+
275+
const isAutomatic =
276+
flavorUsesAutomaticObjectSerialization(Flavor)
277+
278+
when not isAutomatic:
279+
const
280+
flavor =
281+
"JsonReader[" & typetraits.name(typeof(r).Flavor) & "], " &
282+
typetraits.name(T)
283+
{.error:
284+
"Missing Json serialization import or implementation for readValue(" &
285+
flavor & ")".}
286+
287+
readRecordValue(r, value)
244288

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

263307
when value is JsonString:
264-
autoSerializeCheck(Flavor, JsonString)
265-
value = r.parseAsString()
308+
autoSerializeCheck(Flavor, JsonString):
309+
value = r.parseAsString()
266310

267311
elif value is JsonNode:
268-
autoSerializeCheck(Flavor, JsonNode)
269-
value = r.parseJsonNode()
312+
autoSerializeCheck(Flavor, JsonNode):
313+
value = r.parseJsonNode()
270314

271315
elif value is JsonNumber:
272-
autoSerializeCheck(Flavor, JsonNumber)
273-
r.parseNumber(value)
316+
autoSerializeCheck(Flavor, JsonNumber):
317+
r.parseNumber(value)
274318

275319
elif value is JsonVoid:
276-
autoSerializeCheck(Flavor, JsonVoid)
277-
r.skipSingleJsValue()
320+
autoSerializeCheck(Flavor, JsonVoid):
321+
r.skipSingleJsValue()
278322

279323
elif value is JsonValueRef:
280-
autoSerializeCheck(Flavor, JsonValueRef)
281-
r.parseValue(value)
324+
autoSerializeCheck(Flavor, JsonValueRef):
325+
r.parseValue(value)
282326

283327
elif value is string:
284-
autoSerializeCheck(Flavor, string)
285-
value = r.parseString()
328+
autoSerializeCheck(Flavor, string):
329+
value = r.parseString()
286330

287331
elif value is seq[char]:
288-
autoSerializeCheck(Flavor, seq[char])
289-
let val = r.parseString()
290-
value.setLen(val.len)
291-
for i in 0..<val.len:
292-
value[i] = val[i]
332+
autoSerializeCheck(Flavor, seq[char]):
333+
let val = r.parseString()
334+
value.setLen(val.len)
335+
for i in 0..<val.len:
336+
value[i] = val[i]
293337

294338
elif isCharArray(value):
295-
autoSerializeCheck(Flavor, array, typeof(value))
296-
let val = r.parseString()
297-
if val.len != value.len:
298-
# Raise tkString because we expected a `"` earlier
299-
r.raiseUnexpectedToken(etString)
300-
for i in 0..<value.len:
301-
value[i] = val[i]
339+
autoSerializeCheck(Flavor, array, typeof(value)):
340+
let val = r.parseString()
341+
if val.len != value.len:
342+
# Raise tkString because we expected a `"` earlier
343+
r.raiseUnexpectedToken(etString)
344+
for i in 0..<value.len:
345+
value[i] = val[i]
302346

303347
elif value is bool:
304-
autoSerializeCheck(Flavor, bool)
305-
value = r.parseBool()
348+
autoSerializeCheck(Flavor, bool):
349+
value = r.parseBool()
306350

307-
elif value is ref|ptr:
308-
when value is ref:
309-
autoSerializeCheck(Flavor, ref, typeof(value))
351+
elif value is ref:
352+
autoSerializeCheck(Flavor, ref, typeof(value)):
353+
readValueRefOrPtr(r, value)
310354

311-
when value is ptr:
312-
autoSerializeCheck(Flavor, ptr, typeof(value))
313-
314-
when compiles(isNotNilCheck(value)):
315-
allocPtr value
316-
value[] = readValue(r, type(value[]))
317-
else:
318-
if r.tokKind == JsonValueKind.Null:
319-
value = nil
320-
r.parseNull()
321-
else:
322-
allocPtr value
323-
value[] = readValue(r, type(value[]))
355+
elif value is ptr:
356+
autoSerializeCheck(Flavor, ptr, typeof(value)):
357+
readValueRefOrPtr(r, value)
324358

325359
elif value is enum:
326-
autoSerializeCheck(Flavor, enum, typeof(value))
327-
r.parseEnum(value)
360+
autoSerializeCheck(Flavor, enum, typeof(value)):
361+
r.parseEnum(value)
328362

329363
elif value is SomeInteger:
330-
autoSerializeCheck(Flavor, SomeInteger, typeof(value))
331-
value = r.parseInt(typeof value,
332-
JsonReaderFlag.portableInt in r.lex.flags)
364+
autoSerializeCheck(Flavor, SomeInteger, typeof(value)):
365+
value = r.parseInt(typeof value,
366+
JsonReaderFlag.portableInt in r.lex.flags)
333367

334368
elif value is SomeFloat:
335-
autoSerializeCheck(Flavor, SomeFloat, typeof(value))
336-
let val = r.parseNumber(uint64)
337-
if val.isFloat:
338-
value = r.toFloat(val, typeof value)
339-
else:
340-
value = T(val.integer)
369+
autoSerializeCheck(Flavor, SomeFloat, typeof(value)):
370+
let val = r.parseNumber(uint64)
371+
if val.isFloat:
372+
value = r.toFloat(val, typeof value)
373+
else:
374+
value = T(val.integer)
341375

342376
elif value is seq:
343-
autoSerializeCheck(Flavor, seq, typeof(value))
344-
r.parseArray:
345-
let lastPos = value.len
346-
value.setLen(lastPos + 1)
347-
readValue(r, value[lastPos])
377+
autoSerializeCheck(Flavor, seq, typeof(value)):
378+
r.parseArray:
379+
let lastPos = value.len
380+
value.setLen(lastPos + 1)
381+
readValue(r, value[lastPos])
348382

349383
elif value is array:
350-
autoSerializeCheck(Flavor, array, typeof(value))
351-
type IDX = typeof low(value)
352-
r.parseArray(idx):
353-
if idx < value.len:
354-
let i = IDX(idx + low(value).int)
355-
readValue(r, value[i])
356-
else:
357-
r.raiseUnexpectedValue("Too many items for " & $(typeof(value)))
358-
359-
elif value is (object or tuple):
360-
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey
361-
when value is object:
362-
autoSerializeCheck(Flavor, object, typeof(value))
363-
364-
when value is tuple:
365-
autoSerializeCheck(Flavor, tuple, typeof(value))
366-
367-
# Keep existing object/tuple auto serialization
368-
# But make it deprecated.
369-
mixin flavorUsesAutomaticObjectSerialization
384+
autoSerializeCheck(Flavor, array, typeof(value)):
385+
type IDX = typeof low(value)
386+
r.parseArray(idx):
387+
if idx < value.len:
388+
let i = IDX(idx + low(value).int)
389+
readValue(r, value[i])
390+
else:
391+
r.raiseUnexpectedValue("Too many items for " & $(typeof(value)))
392+
393+
elif value is object:
394+
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey and cannot accept `object` param
395+
autoSerializeCheck(Flavor, object, typeof(value)):
396+
readValueObjectOrTuple(Flavor, r, value)
397+
else:
398+
readValueObjectOrTuple(Flavor, r, value)
370399

371-
const isAutomatic =
372-
flavorUsesAutomaticObjectSerialization(Flavor)
400+
elif value is tuple:
401+
when declared(macrocache.hasKey): # Nim 1.6 have no macrocache.hasKey and cannot accept `tuple` param
402+
autoSerializeCheck(Flavor, tuple, typeof(value)):
403+
readValueObjectOrTuple(Flavor, r, value)
404+
else:
405+
readValueObjectOrTuple(Flavor, r, value)
373406

374-
when not isAutomatic:
375-
const
376-
flavor =
377-
"JsonReader[" & typetraits.name(typeof(r).Flavor) & "], " &
378-
typetraits.name(T)
379-
{.error:
380-
"Missing Json serialization import or implementation for readValue(" &
381-
flavor & ")".}
382-
383-
readRecordValue(r, value)
384407
else:
385-
const typeName = typetraits.name(T)
386-
{.error: "Failed to convert to JSON an unsupported type: " &
408+
const
409+
typeName = typetraits.name(T)
410+
flavorName = typetraits.name(Flavor)
411+
{.error: flavorName & ": Failed to convert from JSON an unsupported type: " &
387412
typeName.}
388413

389414
iterator readObjectFields*(r: var JsonReader): string {.

0 commit comments

Comments
 (0)