core/encoding/json
encoding_json
Types
30Array
Array :: [dynamic]ValueSourceBoolean
Boolean :: boolSourceError
Error :: enum int {
None = 0,
EOF = 1, // Not necessarily an error
// Tokenizing Errors
Illegal_Character = 2,
Invalid_Number = 3,
String_Not_Terminated = 4,
Invalid_String = 5,
Invalid_Rune = 6,
// Parsing Errors
Unexpected_Token = 7,
Expected_String_For_Object_Key = 8,
Duplicate_Object_Key = 9,
Expected_Colon_After_Key = 10,
// Allocating Errors
Invalid_Allocator = 11,
Out_Of_Memory = 12,
}SourceFloat
Float :: f64SourceInteger
Integer :: i64SourceMarshal_Data_Error
Marshal_Data_Error :: enum int {
None = 0,
Unsupported_Type = 1,
}SourceMarshal_Error
Marshal_Error :: union {
Marshal_Data_Error,
io.Error,
}SourceMarshal_Options
Marshal_Options :: struct {
// output based on spec
spec: Specification,
// Use line breaks & tabs/spaces
pretty: bool,
// Use spaces for indentation instead of tabs
use_spaces: bool,
// Given use_spaces true, use this many spaces per indent level. 0 means 4 spaces.
spaces: int,
// Output uint as hex in JSON5 & MJSON
write_uint_as_hex: bool,
// If spec is MJSON and this is true, then keys will be quoted.
//
// WARNING: If your keys contain whitespace and this is false, then the
// output will be bad.
mjson_keys_use_quotes: bool,
// If spec is MJSON and this is true, then use '=' as delimiter between
// keys and values, otherwise ':' is used.
mjson_keys_use_equal_sign: bool,
// When outputting a map, sort the output by key.
//
// NOTE: This will temp allocate and sort a list for each map.
sort_maps_by_key: bool,
// Output enum value's name instead of its underlying value.
//
// NOTE: If a name isn't found it'll use the underlying value.
use_enum_names: bool,
// Internal state
indentation: int,
mjson_skipped_first_braces_start: bool,
mjson_skipped_first_braces_end: bool,
}Sourcecareful with MJSON maps & non quotes usage as keys with whitespace will lead to bad results
Match_Error
Match_Error :: enum int {
None = 0,
Invalid_Argument = 1,
Invalid_Type_For_Index = 2,
Invalid_Type_For_Key = 3,
Key_Not_Found = 4,
Out_Of_Bounds_Index = 5,
}SourceMatch_Flag
Match_Flag :: enum int {
Ignore_Key_Not_Found = 0,
Allow_String_Indexing_By_Byte = 1,
}SourceMatch_Flags
Match_Flags :: bit_set[Match_Flag; 0..1]SourceMatch_Key_Variant
Match_Key_Variant :: union {
int, // Index
string, // Key
}SourceNull
Null :: rawptrSourceObject
Object :: map[string]ValueSourceParser
Parser :: struct {
tok: Tokenizer,
prev_token: Token,
curr_token: Token,
spec: Specification,
allocator: mem.Allocator,
parse_integers: bool,
}SourcePos
Pos :: struct {
offset: int,
line: int,
column: int,
}SourceRegister_User_Marshaler_Error
Register_User_Marshaler_Error :: enum int {
None = 0,
No_User_Marshaler = 1,
Marshaler_Previously_Found = 2,
}SourceRegister_User_Unmarshaler_Error
Register_User_Unmarshaler_Error :: enum int {
None = 0,
No_User_Unmarshaler = 1,
Unmarshaler_Previously_Found = 2,
}SourceSpecification
Specification :: enum int {
JSON = 0,
JSON5 = 1, // https://json5.org/
SJSON = 2, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
Bitsquid = SJSON,
MJSON = SJSON,
}SourceString
String :: stringSourceToken
Token :: struct {
pos: Pos,
kind: Token_Kind,
text: string,
}SourceToken_Kind
Token_Kind :: enum int {
Invalid = 0,
EOF = 1,
Null = 2,
False = 3,
True = 4,
Infinity = 5,
NaN = 6,
Ident = 7,
Integer = 8,
Float = 9,
String = 10,
Colon = 11,
Comma = 12,
Open_Brace = 13,
Close_Brace = 14,
Open_Bracket = 15,
Close_Bracket = 16,
}SourceTokenizer
Tokenizer :: struct {
pos: Pos,
data: string,
r: rune,
w: int,
curr_line_offset: int,
spec: Specification,
parse_integers: bool,
insert_comma: bool,
}SourceUnmarshal_Data_Error
Unmarshal_Data_Error :: enum int {
Invalid_Data = 0,
Invalid_Parameter = 1,
Non_Pointer_Parameter = 2,
Multiple_Use_Field = 3,
}SourceUnmarshal_Error
Unmarshal_Error :: union {
Error,
Unmarshal_Data_Error,
Unsupported_Type_Error,
}SourceUnparse_Error
Unparse_Error :: union {
io.Error,
runtime.Allocator_Error,
}SourceUnsupported_Type_Error
Unsupported_Type_Error :: struct {
id: typeid,
token: Token,
}SourceUser_Marshaler
User_Marshaler :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (Marshal_Error)SourceUser_Unmarshaler
User_Unmarshaler :: proc(p: ^Parser, v: any) -> (Unmarshal_Error)SourceValue
Value :: union {
Null,
Integer,
Float,
Boolean,
String,
Array,
Object,
}SourceConstants
1Variables
2_user_marshalers
_user_marshalers :: ^map[typeid]User_MarshalerSourceExample User Marshaler: Custom Marshaler for int Some_Marshaler :: proc(w: io.Writer, v: any, opt: ^json.Marshal_Options) -> json.Marshal_Error {
io.write_string(w, fmt.tprintf("%b", v))
return json.Marshal_Data_Error.None
}
main :: proc() {
// Ensure the json._user_marshaler map is initialized
json.set_user_marshalers(new(map[typeid]json.User_Marshaler))
reg_err := json.register_user_marshaler(type_info_of(int).id, Some_Marshaler)
assert(reg_err == .None)
// Use the custom marshaler
SomeType :: struct {
value: int,
}
x := SomeType{42}
data, marshal_err := json.marshal(x)
assert(marshal_err == nil)
defer delete(data)
fmt.println("Custom output:", string(data)) // Custom output: {"value":101010}
}
NOTE(Jeroen): This is a pointer to prevent accidental additions
it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary_user_unmarshalers
_user_unmarshalers :: ^map[typeid]User_UnmarshalerSourceNOTE(Jeroen): This is a pointer to prevent accidental additions it is prefixed with _ rather than marked with a private attribute so that users can access it if necessary
Procedures
50advance_token
advance_token :: proc(p: ^Parser) -> (Error, Token)Sourceallow_token
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> (bool)Sourceclone_string
clone_string :: proc(s: string, allocator: mem.Allocator, loc = #caller_location) -> (str: string, err: Error)Sourceclone_value
clone_value :: proc(value: Value, allocator: mem.Allocator = context.allocator) -> (Value)Sourcedestroy_value
destroy_value :: proc(value: Value, allocator: mem.Allocator = context.allocator, loc = #caller_location)Sourceexpect_token
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> (Error)Sourceget_token
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error)Sourceis_valid
is_valid :: proc(data: []u8, spec = DEFAULT_SPECIFICATION, parse_integers: untyped boolean = false) -> (bool)SourceNOTE(bill): is_valid will not check for duplicate keys
is_valid_number
is_valid_number :: proc(str: string, spec: Specification) -> (bool)Sourceis_valid_string_literal
is_valid_string_literal :: proc(str: string, spec: Specification) -> (bool)Sourcemake_parser_from_bytes
make_parser_from_bytes :: proc(data: []u8, spec = DEFAULT_SPECIFICATION, parse_integers: untyped boolean = false, allocator: mem.Allocator = context.allocator) -> (Parser)Sourcemake_parser_from_string
make_parser_from_string :: proc(data: string, spec = DEFAULT_SPECIFICATION, parse_integers: untyped boolean = false, allocator: mem.Allocator = context.allocator) -> (Parser)Sourcemake_tokenizer
make_tokenizer :: proc(data: string, spec = DEFAULT_SPECIFICATION, parse_integers: untyped boolean = false) -> (Tokenizer)Sourcemarshal
marshal :: proc(v: any, opt: Marshal_Options, allocator: mem.Allocator = context.allocator, loc = #caller_location) -> (data: []u8, err: Marshal_Error)Sourcemarshal_to_builder
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> (Marshal_Error)Sourcemarshal_to_writer
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error)Sourcematch
match :: proc(value: Value, args, flags: Match_Flags) -> (found: Value, err: Match_Error)Sourcenext_rune
next_rune :: proc(t: ^Tokenizer) -> (rune)Sourceopt_write_comment
opt_write_comment :: proc(w: io.Writer, opt: ^Marshal_Options, comment: ^string) -> (err: io.Error)SourceNewlines are split into multiple comment lines
opt_write_end
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: u8) -> (err: io.Error)Sourcedecrease indent, write spacing and insert end byte
opt_write_indentation
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error)Sourcewrites current indentation level based on options
opt_write_iteration
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, first_iteration: bool) -> (err: io.Error)Sourceinsert comma separation and write indentations
opt_write_key
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error)Sourcewrite key as quoted string or with optional quotes in mjson
opt_write_start
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: u8) -> (err: io.Error)Sourceinsert start byte and increase indentation on pretty
parse_array
parse_array :: proc(p: ^Parser, loc = #caller_location) -> (value: Value, err: Error)Sourceparse_bytes
parse_bytes :: proc(data: []u8, spec = DEFAULT_SPECIFICATION, parse_integers: untyped boolean = false, allocator: mem.Allocator = context.allocator, loc = #caller_location) -> (Error, Value)Sourceparse_colon
parse_colon :: proc(p: ^Parser) -> (err: Error)Sourceparse_comma
parse_comma :: proc(p: ^Parser) -> (do_break: bool)Sourceparse_object
parse_object :: proc(p: ^Parser, loc = #caller_location) -> (value: Value, err: Error)Sourceparse_object_body
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc = #caller_location) -> (obj: Object, err: Error)Sourceparse_object_key
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc = #caller_location) -> (key: string, err: Error)Sourceparse_string
parse_string :: proc(data: string, spec = DEFAULT_SPECIFICATION, parse_integers: untyped boolean = false, allocator: mem.Allocator = context.allocator, loc = #caller_location) -> (Error, Value)Sourceparse_value
parse_value :: proc(p: ^Parser, loc = #caller_location) -> (value: Value, err: Error)Sourceregister_user_marshaler
register_user_marshaler :: proc(id: typeid, marshaler: User_Marshaler) -> (Register_User_Marshaler_Error)SourceRegisters a user-defined marshaler for a specific typeid
Inputs:
- id: The typeid of the custom type.
- formatter: The User_Marshaler function for the custom type.
Returns: A Register_User_Marshaler_Error value indicating the success or failure of the operation.
WARNING: set_user_marshalers must be called before using this procedure.
register_user_unmarshaler
register_user_unmarshaler :: proc(id: typeid, unmarshaler: User_Unmarshaler) -> (Register_User_Unmarshaler_Error)SourceRegisters a user-defined unmarshaler for a specific typeid.
WARNING: set_user_unmarshalers must be called before using this procedure.
Inputs:
- id: The
typeidof the custom type. - unmarshaler: The
User_Unmarshalerfunction for the custom type.
Example:
import "core:fmt"
import "core:encoding/json"
import "core:strconv"
// Custom Unmarshaler for `int`
some_unmarshaler :: proc(p: ^json.Parser, v: any) -> json.Unmarshal_Error {
token := p.curr_token.text
i, ok := strconv.parse_i64_of_base(token, 2)
if !ok {
return .Invalid_Data
}
(^int)(v.data)^ = int(i)
json.advance_token(p)
return nil
}
register_user_unmarshaler_example :: proc() {
// Ensure the `json._user_unmarshalers` map is initialized.
json.set_user_unmarshalers(new(map[typeid]json.User_Unmarshaler))
reg_err := json.register_user_unmarshaler(typeid_of(int), some_unmarshaler)
assert(reg_err == .None)
data := `{"value":101010}`
SomeType :: struct {
value: int,
}
y: SomeType
unmarshal_err := json.unmarshal(transmute([]byte)data, &y)
fmt.println(y, unmarshal_err)
}Output:
SomeType{value = 42} nilset_user_marshalers
set_user_marshalers :: proc(m: ^map[typeid]User_Marshaler)SourceSets user-defined marshalers for custom json marshaling of specific types
Inputs:
- m: A pointer to a map of typeids to User_Marshaler procs.
NOTE: Must be called before using register_user_marshaler.
set_user_unmarshalers
set_user_unmarshalers :: proc(m: ^map[typeid]User_Unmarshaler)SourceSets user-defined unmarshalers for custom json unmarshaling of specific types
Inputs:
- m: A pointer to a map of typeids to User_Unmarshaler procs.
NOTE: Must be called before using register_user_unmarshaler.
token_end_pos
token_end_pos :: proc(tok: Token) -> (Pos)Sourceunmarshal
unmarshal :: proc(data: []u8, ptr: ^T, spec = DEFAULT_SPECIFICATION, allocator: mem.Allocator = context.allocator) -> (Unmarshal_Error)Sourceunmarshal_any
unmarshal_any :: proc(data: []u8, v: any, spec = DEFAULT_SPECIFICATION, allocator: mem.Allocator = context.allocator) -> (Unmarshal_Error)Sourceunmarshal_string
unmarshal_string :: proc(data: string, ptr: ^T, spec = DEFAULT_SPECIFICATION, allocator: mem.Allocator = context.allocator) -> (Unmarshal_Error)Sourceunparse
unparse :: proc(v: Value, opt: Marshal_Options, allocator: mem.Allocator = context.allocator, loc = #caller_location) -> (data: string, err: Unparse_Error)Sourceunparse_to_builder
unparse_to_builder :: proc(b: ^strings.Builder, v: Value, opt: ^Marshal_Options) -> (Unparse_Error)Sourceunparse_to_writer
unparse_to_writer :: proc(w: io.Writer, value: Value, opt: ^Marshal_Options) -> (Unparse_Error)Sourceunquote_string
unquote_string :: proc(token: Token, spec: Specification, allocator: mem.Allocator = context.allocator, loc = #caller_location) -> (value: string, err: Error)SourceIMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
validate_array
validate_array :: proc(p: ^Parser) -> (bool)Sourcevalidate_object
validate_object :: proc(p: ^Parser) -> (bool)Sourcevalidate_object_body
validate_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (bool)Sourcevalidate_object_key
validate_object_key :: proc(p: ^Parser) -> (bool)Sourcevalidate_value
validate_value :: proc(p: ^Parser) -> (bool)Source