# Working Without Exceptions By default, the library reports errors by throwing exceptions (see [Exceptions](../home/exceptions.md)). This page collects what to use when exceptions are unwanted or unavailable, for example in embedded systems, game engines, or code compiled with `-fno-exceptions`. ## Two different questions 1. **Can my code avoid *catching* exceptions?** Yes. For parsing and for accessing values there are functions that report failure through a return value. They work in a normal build and need no `#!cpp try`/`#!cpp catch`. 2. **Can the library be *compiled* without exceptions?** Yes, with `-fno-exceptions` or by defining [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md). Both have the same effect: every `#!cpp throw` in the library becomes a call to `std::abort()`. The functions listed below never reach a `#!cpp throw` for the failures they report, so they keep working in this configuration. Anything else that fails aborts the program. ## Parsing | function | on invalid input | |---------------------------------------------------------------------------------------------|-----------------------------------------------------| | `#!cpp json::parse(input, nullptr, false)` | returns a *discarded* value; test with `is_discarded()` | | [`accept`](../api/basic_json/accept.md) | returns `#!cpp false` | | `#!cpp json::from_cbor(input, true, false)` (and the other `from_*` functions) | returns a *discarded* value | | [`sax_parse`](../api/basic_json/sax_parse.md) with your own handler | returns `#!cpp false` after calling `parse_error` | ??? example "Example: parse and validate without exceptions" ```cpp --8<-- "examples/no_exceptions_parse.cpp" ``` Output: ``` --8<-- "examples/no_exceptions_parse.output" ``` None of the calls in this example throws, so it also compiles and runs with `-fno-exceptions`. The discarded value carries no message. If you need the position or the reason, use the SAX interface; see [Parsing and exceptions](parsing/parse_exceptions.md). ## Accessing values The access functions are not equally safe. Only some of them never throw: | expression | if the key is missing or the type is wrong | |-------------------------------------|-------------------------------------------------------------------------------| | `#!cpp j.contains("k")` | returns `#!cpp false` for a missing key; never throws | | `#!cpp j.find("k")` | returns `end()` for a missing key; never throws | | `#!cpp j.is_string()`, ... | the `is_*` functions test the type; never throw | | `#!cpp j.get_ptr()` | returns `#!cpp nullptr` for a different type; never throws | | `#!cpp j.at("k")` | throws `out_of_range.403` for a missing key | | `#!cpp j.get()` | throws `type_error.302` for a value that is not a number | | `#!cpp j.value("k", 0)` | returns the default for a missing key, but **throws `type_error.302` if `k` exists with another type** | | `#!cpp j["k"]` (non-const) | inserts `null` for a missing key | | `#!cpp j["k"]` (const) | undefined behavior for a missing key | So reading a value that may be absent or of the wrong type without exceptions takes two steps: look up, then test the type. ??? example "Example: look up a key and test its type" ```cpp --8<-- "examples/no_exceptions_access.cpp" ``` Output: ``` --8<-- "examples/no_exceptions_access.output" ``` The last lines of the example show why `value` is not on the safe list: it returns the default only for a *missing* key. For numbers, test with `is_number()` (or `is_number_integer()`, `is_number_unsigned()`, `is_number_float()`) before calling `get()`, and see [number handling](types/number_handling.md) for range and precision. Whether a value fits into the target type is not checked by `get`. ## Converting to your own types `from_json` functions (including the ones generated by the [`NLOHMANN_DEFINE_TYPE_*` macros](arbitrary_types.md#simplify-your-life-with-macros)) report problems by throwing. There is no variant that returns an error code. Without exceptions you have two options: - Validate the JSON value yourself, as shown above, and fill your struct field by field. - Convert in a function that is compiled **with** exceptions enabled and catches them at the boundary, returning `std::optional` or an error code to the rest of your code: ??? example "Example: convert to a struct and report failure as std::optional" ```cpp --8<-- "examples/no_exceptions_convert.c++17.cpp" ``` Output: ``` --8<-- "examples/no_exceptions_convert.c++17.output" ``` ## Overriding what happens instead of an exception With exceptions off, you can replace the `abort()` by your own code, for example to log the message first. See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) and [`JSON_THROW_USER`](../api/macros/json_throw_user.md). The handler must not return normally. ## See also - [Exceptions](../home/exceptions.md) - all exceptions and their ids - [Parsing and exceptions](parsing/parse_exceptions.md) - the parsing alternatives in detail - [Parsing untrusted input](parsing/untrusted_input.md) - harden an application that parses data it does not control - [`JSON_NOEXCEPTION`](../api/macros/json_noexception.md) - disable exceptions in the library - [`exception`](../api/basic_json/exception.md) - base class of all exceptions thrown by the library - [`JSON_THROW_USER`](../api/macros/json_throw_user.md) - replace the throw statements of the library - [`parse`](../api/basic_json/parse.md) - deserialize from a compatible input - [`accept`](../api/basic_json/accept.md) - check whether the input is valid JSON - [Supported Macros](macros.md) - all supported macros