Files
Odin/core/encoding/json/unparse.odin
2026-06-23 13:14:56 +01:00

90 lines
2.6 KiB
Odin

package encoding_json
import "base:runtime"
import "core:strings"
import "core:io"
import "core:slice"
Unparse_Error :: union #shared_nil {
io.Error,
runtime.Allocator_Error,
}
@(require_results)
unparse :: proc(v: Value, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: string, err: Unparse_Error) {
b := strings.builder_make(allocator, loc)
defer if err != nil {
strings.builder_destroy(&b)
}
// temp guard in case we are sorting map keys, which will use temp allocations
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
opt := opt
unparse_to_builder(&b, v, &opt) or_return
data = string(b.buf[:])
return
}
@(require_results)
unparse_to_builder :: proc(b: ^strings.Builder, v: Value, opt: ^Marshal_Options) -> Unparse_Error {
return unparse_to_writer(strings.to_writer(b), v, opt)
}
@(require_results)
unparse_to_writer :: proc(w: io.Writer, value: Value, opt: ^Marshal_Options) -> Unparse_Error {
switch v in value {
case nil, Null:
io.write_string(w, "null") or_return
case Integer:
base := 16 if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) else 10
io.write_i64(w, v, base) or_return
case Float:
io.write_f64(w, v) or_return
case Boolean:
io.write_string(w, "true" if v else "false") or_return
case String:
io.write_quoted_string(w, v, '"', nil, true) or_return
case Array:
opt_write_start(w, opt, '[') or_return
for e, i in v {
opt_write_iteration(w, opt, i == 0) or_return
unparse_to_writer (w, e, opt) or_return
}
opt_write_end(w, opt, ']') or_return
case Object:
if !opt.sort_maps_by_key {
opt_write_start(w, opt, '{') or_return
for first_iteration := true; key, val in v {
opt_write_iteration(w, opt, first_iteration) or_return
opt_write_key (w, opt, key) or_return
unparse_to_writer (w, val, opt) or_return
first_iteration = false
}
opt_write_end(w, opt, '}') or_return
} else {
Map_Entry :: struct {
key: string,
value: Value,
}
entries := make([dynamic]Map_Entry, 0, len(v), context.temp_allocator) or_return
for key, val in v {
_, _ = append(&entries, Map_Entry{key, val})
}
slice.sort_by(entries[:], proc(i, j: Map_Entry) -> bool { return i.key < j.key })
opt_write_start(w, opt, '{') or_return
for e, i in entries {
opt_write_iteration(w, opt, i == 0) or_return
opt_write_key (w, opt, e.key) or_return
unparse_to_writer (w, e.value, opt) or_return
}
opt_write_end(w, opt, '}') or_return
}
return nil
}
return nil
}