4141 is_decoded bool
4242}
4343
44- // Decoder represents a JSON decoder.
44+ // DecoderOptions provides options for JSON decoding.
45+ // By default, decoding is lenient. Use `strict: true` for strict JSON spec compliance.
46+ @[params]
47+ pub struct DecoderOptions {
48+ pub :
49+ // In strict mode, quoted strings are not accepted as numbers.
50+ // For example, '"123"' will fail to decode as int in strict mode,
51+ // but will succeed in default mode.
52+ strict bool
53+ }
54+
55+ // Decoder is the internal decoding state.
4556struct Decoder {
46- json string // json is the JSON data to be decoded.
57+ json string // json is the JSON data to be decoded.
58+ strict bool // strict mode rejects quoted strings as numbers
4759mut :
4860 values_info LinkedList[ValueInfo] // A linked list to store ValueInfo.
4961 checker_idx int // checker_idx is the current index of the decoder.
@@ -271,8 +283,9 @@ fn (mut decoder Decoder) decode_error(message string) ! {
271283}
272284
273285// decode decodes a JSON string into a specified type.
286+ // By default, decoding is lenient. Use `strict: true` for strict JSON spec compliance.
274287@[manualfree]
275- pub fn decode [T](val string ) ! T {
288+ pub fn decode [T](val string , params DecoderOptions ) ! T {
276289 if val == '' {
277290 return JsonDecodeError{
278291 message: 'empty string'
@@ -281,7 +294,8 @@ pub fn decode[T](val string) !T {
281294 }
282295 }
283296 mut decoder := Decoder{
284- json: val
297+ json: val
298+ strict: params.strict
285299 }
286300
287301 decoder.check_json_format ()!
@@ -627,12 +641,9 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! {
627641
628642 if value_info.value_kind == .number {
629643 unsafe { decoder.decode_number (& val)! }
630- } else if value_info.value_kind == .string {
631- // recheck if string contains number
632- decoder.checker_idx = value_info.position + 1
633- decoder.check_number ()!
634-
635- unsafe { decoder.decode_number (& val)! }
644+ } else if value_info.value_kind == .string && ! decoder.strict {
645+ // In default mode, try to parse quoted strings as numbers
646+ val = decoder.decode_number_from_string[T]()!
636647 } else {
637648 decoder.decode_error ('Expected number, but got ${value_info .value_kind }' )!
638649 }
@@ -856,15 +867,7 @@ fn (mut decoder Decoder) decode_enum[T](mut val T) ! {
856867// use pointer instead of mut so enum cast works
857868@[unsafe ]
858869fn (mut decoder Decoder) decode_number [T](val & T) ! {
859- mut number_info := decoder.current_node.value
860-
861- if decoder.json[number_info.position] == `"` { // fake number
862- number_info = ValueInfo{
863- position: number_info.position + 1
864- length: number_info.length - 2
865- }
866- }
867-
870+ number_info := decoder.current_node.value
868871 str := decoder.json[number_info.position..number_info.position + number_info.length]
869872 $match T.unaliased_typ {
870873 i8 { * val = strconv.atoi8 (str)! }
@@ -883,3 +886,43 @@ fn (mut decoder Decoder) decode_number[T](val &T) ! {
883886 $else { return error ('`decode_number` can not decode ${T .name } type' ) }
884887 }
885888}
889+
890+ // decode_number_from_string parses a number from a JSON string value (default mode).
891+ // This extracts the content between quotes and parses it as a number.
892+ fn (mut decoder Decoder) decode_number_from_string [T]() ! T {
893+ string_info := decoder.current_node.value
894+ // Extract string content without quotes (position+1 to skip opening quote, length-2 to exclude both quotes)
895+ if string_info.length < 2 {
896+ return error ('invalid string for number conversion' )
897+ }
898+ str := decoder.json[string_info.position + 1 ..string_info.position + string_info.length - 1 ]
899+ $if T.unaliased_typ is i8 {
900+ return T (strconv.atoi8 (str)! )
901+ } $else $if T.unaliased_typ is i16 {
902+ return T (strconv.atoi16 (str)! )
903+ } $else $if T.unaliased_typ is i32 {
904+ return T (strconv.atoi32 (str)! )
905+ } $else $if T.unaliased_typ is i64 {
906+ return T (strconv.atoi64 (str)! )
907+ } $else $if T.unaliased_typ is u8 {
908+ return T (strconv.atou8 (str)! )
909+ } $else $if T.unaliased_typ is u16 {
910+ return T (strconv.atou16 (str)! )
911+ } $else $if T.unaliased_typ is u32 {
912+ return T (strconv.atou32 (str)! )
913+ } $else $if T.unaliased_typ is u64 {
914+ return T (strconv.atou64 (str)! )
915+ } $else $if T.unaliased_typ is int {
916+ return T (strconv.atoi (str)! )
917+ } $else $if T.unaliased_typ is isize {
918+ return T (isize (strconv.atoi64 (str)! ))
919+ } $else $if T.unaliased_typ is usize {
920+ return T (usize (strconv.atou64 (str)! ))
921+ } $else $if T.unaliased_typ is f32 {
922+ return T (f32 (strconv.atof_quick (str)))
923+ } $else $if T.unaliased_typ is f64 {
924+ return T (strconv.atof_quick (str))
925+ } $else {
926+ return error ('`decode_number_from_string` cannot decode ${T .name } type' )
927+ }
928+ }
0 commit comments