Repository navigation
contains(json_pointer) and json_pointer / size_t fail to compile with a conforming custom StringType #5666
Description
Description
With a custom StringType that provides exactly the members the documentation lists as required, basic_json::contains(const json_pointer&) and json_pointer::operator/(std::size_t) / operator/=(std::size_t) fail to compile, because they use operations on string_t that are not in the documented requirements:
include/nlohmann/detail/json_pointer.hpp#L749(injson_pointer::contains):"0" <= reference_token && reference_token <= "9"requiresoperator<=betweenconst char[2]andstring_t.include/nlohmann/detail/json_pointer.hpp#L777(injson_pointer::contains):std::strtoull(reference_token.c_str(), ...)requiresc_str().include/nlohmann/detail/json_pointer.hpp#L119(operator/=(std::size_t), also used byoperator/(const json_pointer&, std::size_t)):*this /= std::to_string(array_idx)requiresstring_tto be constructible fromstd::string.
The template parameter documentation states that "c_str() and back() are not required", lists only == and < as required comparisons, and says a std::size_t is converted either via assignment from std::to_string or via an ADL int_to_string (which the program below provides). at(json_pointer) compiles with the same type.
Since when: (1) and (3) date from 2020 (8bfc692, 78e911b) and fail with v3.12.0 as well; (2) was added in #5495 (8e26b9d, 2026-09-09). The requirements were written down in #5443 (c72f37a, 2026-09-15), which added tests/src/unit-alt-string.cpp coverage for other JSON pointer functions but not for these two. With the v3.12.0 headers the program fails with many more errors (for example c_str() in the serializer), so this is not a regression relative to a release.
Separately, the contains(ptr) call emits two -Wdeprecated-declarations warnings ("'operator my_string' is deprecated") from detail::is_comparable evaluating std::less<> between my_string and json_pointer for the contains(KeyType&&) overload; my_string's non-template operator< makes the deprecated json_pointer::operator string_t() viable. This is similar to #4621 (fixed for ordered_json) and is not changed by the fix below.
Related but different: #3388 and #4019 (earlier compile errors of json_pointer and flatten() with alternative string types, both fixed).
The documentation's own reference implementation, custom_string_type.hpp, which the StringType section presents as a type that satisfies every requirement, fails the same way for contains(json_pointer) (the c_str() and "0" <= token errors). The example's own gaps (no way to append a char, needed by json_pointer::to_string(), flatten(), unflatten(), and diff()) are fixed in #5638; contains(json_pointer) still fails with the fixed example, because the cause is in the library.
Possible fix: use only documented members. I tested the following with the program below (C++11 and C++17) and with a quick std::string check of contains() and operator/; I did not run the test suite:
// json_pointer.hpp:119
return *this /= detail::to_string<string_t>(array_idx); // needs #include <nlohmann/detail/string_utils.hpp>
// json_pointer.hpp:749
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !('0' <= reference_token[0] && reference_token[0] <= '9')))
// json_pointer.hpp:777 (data() is documented to be null-terminated)
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10);Adding contains(json_pointer) and operator/(std::size_t) to tests/src/unit-alt-string.cpp would catch this.
Reproduction steps
- Save the program below as
c07.cpp. - Compile it against
develop:clang++ -std=c++17 -I include c07.cpp -o c07(GCC 16.2 reports the same three errors).
Expected vs. actual results
- Expected: the program compiles and prints
1,1,2. - Actual: three compile errors in
json_pointer.hpp; without thecontainsandoperator/lines the program compiles and runs.
Minimal code example
#include <nlohmann/json.hpp>
#include <cstddef>
#include <iostream>
#include <map>
#include <string>
#include <vector>
// a string type with the members docs/mkdocs/docs/features/types/template_parameters.md
// lists as required (no c_str(), no comparison with const char*, not constructible from std::string)
class my_string
{
public:
using value_type = char;
using size_type = std::size_t;
using iterator = std::string::iterator;
using const_iterator = std::string::const_iterator;
static constexpr size_type npos = std::string::npos;
my_string() = default;
my_string(const char* s) : s_(s) {}
my_string(const char* s, size_type n) : s_(s, n) {}
my_string(size_type n, char c) : s_(n, c) {}
size_type size() const noexcept { return s_.size(); }
bool empty() const noexcept { return s_.empty(); }
void clear() noexcept { s_.clear(); }
void reserve(size_type n) { s_.reserve(n); }
void resize(size_type n) { s_.resize(n); }
void resize(size_type n, char c) { s_.resize(n, c); }
const char* data() const noexcept { return s_.data(); }
void push_back(char c) { s_.push_back(c); }
char& operator[](size_type i) { return s_[i]; }
const char& operator[](size_type i) const { return s_[i]; }
my_string& append(const char* s, size_type n) { s_.append(s, n); return *this; }
my_string& append(const my_string& o) { s_.append(o.s_); return *this; }
size_type find_first_of(char c, size_type pos = 0) const { return s_.find_first_of(c, pos); }
iterator begin() { return s_.begin(); }
iterator end() { return s_.end(); }
const_iterator begin() const { return s_.begin(); }
const_iterator end() const { return s_.end(); }
friend bool operator==(const my_string& a, const my_string& b) { return a.s_ == b.s_; }
friend bool operator<(const my_string& a, const my_string& b) { return a.s_ < b.s_; }
friend std::ostream& operator<<(std::ostream& o, const my_string& s) { return o << s.s_; }
private:
std::string s_;
};
void int_to_string(my_string& target, std::size_t value)
{
target = std::to_string(value).c_str();
}
using my_json = nlohmann::basic_json<std::map, std::vector, my_string>;
int main()
{
my_json j = {{"foo", {1, 2}}};
const my_json::json_pointer ptr("/foo/0");
std::cout << j.at(ptr) << '\n'; // compiles
std::cout << j.contains(ptr) << '\n'; // expected: compiles, prints 1
// actual: error: no member named 'c_str' in 'my_string'
// error: invalid operands ('const char[2]' and 'const my_string')
auto p = my_json::json_pointer("/foo") / std::size_t(1); // expected: compiles
// actual: error: no viable overloaded '/='
std::cout << j.at(p) << '\n';
}Error messages
Trimmed to the errors and their first instantiation notes:
include/nlohmann/detail/json_pointer.hpp:749:83: error: invalid operands to binary expression ('const char[2]' and 'const my_string')
749 | if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
| ~~~ ^ ~~~~~~~~~~~~~~~
include/nlohmann/json.hpp:3515:20: note: in instantiation of function template specialization 'nlohmann::json_pointer<my_string>::contains<nlohmann::basic_json<std::map, std::vector, my_string>>' requested here
c07.cpp:64:20: note: in instantiation of member function 'nlohmann::basic_json<std::map, std::vector, my_string>::contains' requested here
include/nlohmann/detail/json_pointer.hpp:777:88: error: no member named 'c_str' in 'my_string'
777 | const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int)
| ~~~~~~~~~~~~~~~ ^
include/nlohmann/detail/json_pointer.hpp:119:22: error: no viable overloaded '/='
119 | return *this /= std::to_string(array_idx);
| ~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~
include/nlohmann/detail/json_pointer.hpp:141:34: note: in instantiation of member function 'nlohmann::json_pointer<my_string>::operator/=' requested here
c07.cpp:68:44: note: in instantiation of member function 'nlohmann::operator/' requested here
2 warnings and 3 errors generated.
Compiler and operating system
Apple clang 21.0.0 (clang-2100.3.34.2), macOS 27.0 (arm64); also GCC 16.2 (/opt/homebrew/bin/g++-16)
Library version
develop @ 633de8e
Validation
- The bug also occurs if the latest version from the
developbranch is used. - I can successfully compile and run the unit tests.
This issue was written by Claude Code on behalf of @nlohmann.
Activity
Use only documented StringType members in json_pointer (nlohmann#5692)