Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.5] - 2026-06-11

### Added
- Added warning callback trigger when attempting to retrieve non-existent keys in `TJValueObject` using `try_get_string`, `get_*(..)`, or `get<>(..)` methods.

### Changed
- Incremented version to 0.3.0.

## [0.2.4] - 2026-05-20

### Added
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![CI](https://github.com/FFMG/TinyJSON/actions/workflows/c-cpp.yml/badge.svg)](https://github.com/FFMG/TinyJSON/actions/workflows/c-cpp.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Version](https://img.shields.io/badge/version-0.2.4-blue.svg)](src/TinyJSON.h)
[![Version](https://img.shields.io/badge/version-0.2.5-blue.svg)](src/TinyJSON.h)

A lightweight and lightning-fast C++ JSON & JSON5 parser designed for high performance and minimal footprint.

Expand Down Expand Up @@ -131,8 +131,8 @@ The version is set in the `TinyJSON.h` file.
```cpp
static const short TJ_VERSION_MAJOR = 0;
static const short TJ_VERSION_MINOR = 2;
static const short TJ_VERSION_PATCH = 4;
static const char TJ_VERSION_STRING[] = "0.2.4";
static const short TJ_VERSION_PATCH = 5;
static const char TJ_VERSION_STRING[] = "0.2.5";
```

### Simple Value Access
Expand Down Expand Up @@ -169,6 +169,7 @@ if (tj) {
* json5_1_0_0
* Callback: (`callback_function:std::function<void(message_type, const TJCHAR*)>`) Callback function called where there is an error, warning etc.
NB: The Callback function is called even if Throw is false.
* If a getter method (`try_get_string`, `get_*(..)`, or `get<>(..)`) is called on a key that does not exist in a `TJValueObject`, a warning message indicating the missing key (e.g. `"The key 'missing' was not found!"`) is passed to the callback function.
* trace
* debug
* info
Expand Down Expand Up @@ -673,7 +674,7 @@ auto doubles = obj->get<std::vector<double>>("floats");
#### Strict Get values


Getter methods on `TJValue` (or through `TJValueObject` keys) can be used to retrieve specific types. By default, these methods are non-strict and return default values if the type is incorrect. By setting `strict: true` in `parse_options`, these methods will throw a `TJParseException` instead.
Getter methods on `TJValue` (or through `TJValueObject` keys) can be used to retrieve specific types. By default, these methods are non-strict and return default values if the type is incorrect. By setting `strict: true` in `parse_options`, these methods will throw a `TJParseException` instead. If a key is not found when calling `try_get_string`, `get_*(..)`, or `get<>(..)`, a warning callback is triggered with a message indicating the key that was not found (e.g. `"The key 'missing' was not found!"`) if a callback function is registered in `parse_options`.

- get_number<T>()
- get_float<T>()
Expand Down
93 changes: 63 additions & 30 deletions src/TinyJSON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5396,16 +5396,68 @@ namespace TinyJSON
return object;
}

void TJValueObject::raise_key_not_found(const TJCHAR* key, bool is_strict) const
{
if (key == nullptr)
{
return;
}
int key_len = 0;
while (key[key_len] != 0)
{
key_len++;
}
int total_len = 9 + key_len + 17;
TJCHAR* msg = new TJCHAR[total_len + 1];

const TJCHAR* prefix = TJCHARPREFIX("The key '");
for (int i = 0; i < 9; ++i)
{
msg[i] = prefix[i];
}

for (int i = 0; i < key_len; ++i)
{
msg[9 + i] = key[i];
}

const TJCHAR* suffix = TJCHARPREFIX("' was not found!");
for (int i = 0; i < 17; ++i)
{
msg[9 + key_len + i] = suffix[i];
}

msg[total_len] = 0;

if (is_strict)
{
_parse_options.callback_function(parse_options::message_type::fatal, msg);
if (_parse_options.throw_exception)
{
#if TJ_USE_CHAR == 1
TJParseException ex(msg);
delete[] msg;
throw ex;
#else
delete[] msg;
throw TJParseException("The key was not found!");
#endif
}
}
else
{
_parse_options.callback_function(parse_options::message_type::warning, msg);
}

delete[] msg;
}

Optional<long double> TJValueObject::get_raw_float(const TJCHAR* key, bool case_sensitive) const
{
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
if (_parse_options.strict)
{
ParseResult _parse_result(_parse_options);
_parse_result.assign_exception_message_and_throw("The key was not found!");
}
raise_key_not_found(key, _parse_options.strict);
return Optional<long double>();
}
return Optional<long double>(value->get_raw_float());
Expand All @@ -5416,11 +5468,7 @@ namespace TinyJSON
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
if (_parse_options.strict)
{
ParseResult _parse_result(_parse_options);
_parse_result.assign_exception_message_and_throw("The key was not found!");
}
raise_key_not_found(key, _parse_options.strict);
return Optional<long long>();
}
return Optional<long long>(value->get_raw_number());
Expand All @@ -5431,11 +5479,7 @@ namespace TinyJSON
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
if (_parse_options.strict)
{
ParseResult _parse_result(_parse_options);
_parse_result.assign_exception_message_and_throw("The key was not found!");
}
raise_key_not_found(key, _parse_options.strict);
return Optional<std::vector<long double>>();
}
return Optional<std::vector<long double>>(value->get_raw_floats());
Expand All @@ -5446,11 +5490,7 @@ namespace TinyJSON
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
if (_parse_options.strict)
{
ParseResult _parse_result(_parse_options);
_parse_result.assign_exception_message_and_throw("The key was not found!");
}
raise_key_not_found(key, _parse_options.strict);
return Optional<std::vector<long long>>();
}
return Optional<std::vector<long long>>(value->get_raw_numbers());
Expand All @@ -5461,11 +5501,7 @@ namespace TinyJSON
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
if (_parse_options.strict)
{
ParseResult _parse_result(_parse_options);
_parse_result.assign_exception_message_and_throw("The key was not found!");
}
raise_key_not_found(key, _parse_options.strict);
return TJCHARPREFIX("");
}
return value->get_string();
Expand All @@ -5476,11 +5512,7 @@ namespace TinyJSON
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
if (_parse_options.strict)
{
ParseResult _parse_result(_parse_options);
_parse_result.assign_exception_message_and_throw("The key was not found!");
}
raise_key_not_found(key, _parse_options.strict);
return false;
}
return value->get_boolean();
Expand Down Expand Up @@ -5898,6 +5930,7 @@ namespace TinyJSON
auto value = try_get_value(key, case_sensitive);
if (nullptr == value)
{
raise_key_not_found(key, false);
return nullptr;
}

Expand Down
8 changes: 5 additions & 3 deletions src/TinyJSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,12 @@
// v0.2.1 - added remove_at to TJValueArray.
// v0.2.2 - added support for Json5 https://github.com/json5/
// v0.2.3 - added atomic file saving
// v0.2.4 - added operator[] and as<T>() accessors to TJValue
// v0.2.4 - added operator[] and as<T>() accessor to TJValue
// v0.2.5 - added raise warning when key is not found.
static const short TJ_VERSION_MAJOR = 0;
static const short TJ_VERSION_MINOR = 2;
static const short TJ_VERSION_PATCH = 4;
static const char TJ_VERSION_STRING[] = "0.2.4";
static const short TJ_VERSION_PATCH = 5;
static const char TJ_VERSION_STRING[] = "0.2.5";

#ifndef TJ_USE_CHAR
# define TJ_USE_CHAR 1
Expand Down Expand Up @@ -1157,6 +1158,7 @@ class TJDictionary;
}

private:
void raise_key_not_found(const TJCHAR* key, bool is_strict) const;
template<typename V>
std::vector<V> get_vector_internal(const TJCHAR* key, bool case_sensitive, std::true_type) const
{
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ set( Sources
"testtinyjsonstrings.cpp"
testtinyjsonvaluesget.cpp
testtinyjsonversion.cpp
testtinyjsonwarnings.cpp
testtinyjsonconstructors.cpp
testtinyjsonwrite.cpp
testtinyjsonset.cpp
Expand Down
2 changes: 1 addition & 1 deletion tests/testtinyjsonexceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ TEST(TestException, GettingNonExistentKeyWillLogAndNotThrow) {
options.strict = true;
options.callback_function = [&](TinyJSON::parse_options::message_type message_type, const TJCHAR* exception_message) {
EXPECT_EQ(message_type, TinyJSON::parse_options::fatal);
EXPECT_STREQ(exception_message, "The key was not found!");
EXPECT_STREQ(exception_message, "The key 'b' was not found!");
called = true;
};
TinyJSON::TJValue* json = TinyJSON::TJ::parse("{\"a\" : 12}", options);
Expand Down
16 changes: 10 additions & 6 deletions tests/testtinyjsonversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@
#define TJ_USE_CHAR 1
#include "../src/TinyJSON.h"

TEST(TestVersion, CheckVersionMajor) {
TEST(TestVersion, CheckVersionMajor)
{
ASSERT_EQ(0, TJ_VERSION_MAJOR);
}

TEST(TestVersion, CheckVersionMinor) {
TEST(TestVersion, CheckVersionMinor)
{
ASSERT_EQ(2, TJ_VERSION_MINOR);
}

TEST(TestVersion, CheckVersionPatch) {
ASSERT_EQ(4, TJ_VERSION_PATCH);
TEST(TestVersion, CheckVersionPatch)
{
ASSERT_EQ(5, TJ_VERSION_PATCH);
}

TEST(TestVersion, CheckVersionString) {
ASSERT_STREQ("0.2.4", TJ_VERSION_STRING);
TEST(TestVersion, CheckVersionString)
{
ASSERT_STREQ("0.2.5", TJ_VERSION_STRING);
}
Loading
Loading