Add null-safe noexcept accessors eval_value/eval_array/eval_object#5193
Open
gitpaladin wants to merge 1 commit into
Open
Add null-safe noexcept accessors eval_value/eval_array/eval_object#5193gitpaladin wants to merge 1 commit into
gitpaladin wants to merge 1 commit into
Conversation
Introduce a small, opt-in header <nlohmann/eval.hpp> providing three free
function templates that complement basic_json::value() with semantics
designed for untrusted JSON payloads:
- eval_value(j, key|ptr, default) -- noexcept, returns default on any
non-matching condition (non-object receiver, missing key/path,
null resolved value, wrong type, conversion failure).
- eval_array(j, key|ptr) -- noexcept, returns const ref to
a static empty array on any non-matching condition.
- eval_object(j, key|ptr) -- noexcept, returns const ref to
a static empty object on any non-matching condition.
Design follows the discussion on nlohmann#5129:
* Implemented as non-member functions in namespace nlohmann so that
ADL works without explicit qualification (eval_value(j, ...) just
works).
* Lives in a dedicated opt-in header so basic_json's already large
public API is not extended (per maintainer preference in the
discussion).
* Uses only the public API of basic_json (is_object, is_array, find,
end, get, contains(json_pointer), at(json_pointer)). The pointer
overloads guard with j.contains(ptr) before j.at(ptr), which is
correct under JSON_NOEXCEPTION too (where at() would otherwise
abort instead of throwing).
* Static empty array/object fallbacks are returned by const reference
via Meyers' singletons, so they incur no per-call allocation and
are thread-safe.
* Header-private NLOHMANN_EVAL_TRY / NLOHMANN_EVAL_CATCH_ALL macros
mirror the JSON_TRY / JSON_INTERNAL_CATCH semantics (the library's
own macros are intentionally undef'd at the end of json.hpp via
macro_unscope.hpp and are therefore unavailable to consumers).
Tests (tests/src/unit-eval.cpp): 7 test cases, 79 assertions, covering
happy paths, missing keys/pointers, null resolved values, wrong
resolved types, non-object receivers, ADL invocation, stable
singleton identity, range-based for safety, and noexcept(...) probes.
Documentation: docs/mkdocs/docs/api/eval.md describes the API,
semantics, comparison with value(), and design notes.
Signed-off-by: gitpaladin <alexander.wuhan@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces an opt-in header
<nlohmann/eval.hpp>with three free function templates that complementbasic_json::value()with semantics designed for untrusted JSON payloads:eval_value(j, key|ptr, default)noexcept, returnsdefaulton any non-matching condition (non-object receiver, missing key/path, null resolved value, wrong type, conversion failure).eval_array(j, key|ptr)noexcept, returns aconst&to a static empty array on any non-matching condition.eval_object(j, key|ptr)noexcept, returns aconst&to a static empty object on any non-matching condition.Follows the design agreed in #5129.
Why non-member, opt-in header
Per your preference in the discussion ("I definitely prefer this over an extension of the (already bloated) basic_json API"):
namespace nlohmannso ADL works without explicit qualification (eval_value(j, ...)just works).basic_json's public surface is not extended.is_object,is_array,is_null,find,end,get,contains(json_pointer),at(json_pointer)).j.contains(ptr)beforej.at(ptr), so they remain correct underJSON_NOEXCEPTIONtoo (whereat()would otherwise abort).NLOHMANN_EVAL_TRY/NLOHMANN_EVAL_CATCH_ALLpair mirrorsJSON_TRY/JSON_INTERNAL_CATCH, since those macros are#undef'd at the end ofjson.hppviamacro_unscope.hppand are not visible to consumers.What is added
include/nlohmann/eval.hpp— 6 overloads (key / json_pointer × value / array / object).tests/src/unit-eval.cpp— 7 test cases, 79 assertions, picked up automatically by the existingfile(GLOB src/unit-*.cpp)intests/CMakeLists.txt.docs/mkdocs/docs/api/eval.md— API page mirroringbasic_json/value.mdstyle.What is not changed
basic_jsonis untouched — no new members, no signature changes, no ABI impact.single_include/nlohmann/json.hppis not modified — the new header is intentionally not bundled into the amalgamated single-include (matches Step 4 of the "Next step" plan in Feature Discussion: Null-safe, noexcept accessors — `eval_value`, `eval_array`, `eval_object` #5129). Happy to extendtools/amalgamate/config_json.jsonif you'd prefer it bundled.Local test matrix
-Wshadow -Wconversion -Wsign-conversion -Wold-style-cast)-fno-exceptions -DJSON_NOEXCEPTIONtest-disabled_exceptions)Checklist
tests/src/unit-eval.cpp(7 cases / 79 assertions).docs/mkdocs/docs/api/.make amalgamate— not run yet because the new header is intentionally not part of the single-include. If you'd like it bundled, I'll extend the amalgamate config and rerun. Will also rerun should the CI's amalgamation check flag any astyle drift.Marked as draft so you can pre-screen the direction; happy to address feedback (naming, single-include bundling, anything else) before un-drafting.