diff --git a/src/TinyJSON.cpp b/src/TinyJSON.cpp index 45b32d5..6f391d7 100644 --- a/src/TinyJSON.cpp +++ b/src/TinyJSON.cpp @@ -4679,6 +4679,11 @@ namespace TinyJSON return false; } + bool TJValue::is_numeric(bool /*strict*/) const + { + return false; + } + const TJCHAR* TJValue::dump(formatting formatting, const TJCHAR* indent) const { free_last_dump(); @@ -5087,6 +5092,63 @@ namespace TinyJSON return true; } + bool TJValueString::is_numeric(bool strict) const + { + if (strict) + { + return false; + } + + const TJCHAR* s = raw_value(); + if (!s || *s == TJ_NULL_TERMINATOR) + { + return false; + } + + bool has_digit = false; + bool has_decimal = false; + bool has_exponent = false; + + if (*s == '+' || *s == '-') + { + s++; + } + + while (*s != TJ_NULL_TERMINATOR) + { + if (std::isdigit(static_cast(*s))) + { + has_digit = true; + } + else if (*s == '.') + { + if (has_decimal || has_exponent) + { + return false; + } + has_decimal = true; + } + else if (*s == 'e' || *s == 'E') + { + if (has_exponent || !has_digit) + { + return false; + } + has_exponent = true; + if (*(s + 1) == '+' || *(s + 1) == '-') + { + s++; + } + } + else + { + return false; + } + s++; + } + return has_digit; + } + void TJValueString::free_value() { if (nullptr != _value) @@ -5173,6 +5235,11 @@ namespace TinyJSON return !_is_true; } + bool TJValueBoolean::is_numeric(bool /*strict*/) const + { + return false; + } + /////////////////////////////////////// /// TJValue null TJValueNull::TJValueNull(const parse_options& options) : @@ -5234,6 +5301,11 @@ namespace TinyJSON return true; } + bool TJValueNull::is_numeric(bool /*strict*/) const + { + return false; + } + /////////////////////////////////////// /// TJValueObject TJValueObject::TJValueObject(const parse_options& options) : @@ -5839,6 +5911,11 @@ namespace TinyJSON return true; } + bool TJValueObject::is_numeric(bool /*strict*/) const + { + return false; + } + unsigned int TJValueObject::get_number_of_items() const { if (_members == nullptr) @@ -6310,6 +6387,11 @@ namespace TinyJSON return true; } + bool TJValueArray::is_numeric(bool /*strict*/) const + { + return false; + } + unsigned int TJValueArray::get_number_of_items() const { if (_values == nullptr) @@ -6630,6 +6712,11 @@ namespace TinyJSON return true; } + bool TJValueNumber::is_numeric(bool /*strict*/) const + { + return true; + } + long double TJValueNumber::get_float() const { auto value_float = dynamic_cast(this); @@ -7185,6 +7272,11 @@ namespace TinyJSON return true; } + bool TJValueComment::is_numeric(bool /*strict*/) const + { + return false; + } + const TJCHAR* TJValueComment::raw_value() const { return _value == nullptr ? TJCHARPREFIX("") : _value; diff --git a/src/TinyJSON.h b/src/TinyJSON.h index 3376abf..b54965d 100644 --- a/src/TinyJSON.h +++ b/src/TinyJSON.h @@ -543,6 +543,7 @@ class TJDictionary; virtual bool is_false() const; virtual bool is_null() const; virtual bool is_comment() const; + virtual bool is_numeric(bool strict = false) const; const TJCHAR* dump(formatting formatting = formatting::indented, const TJCHAR* indent = TJCHARPREFIX(" ")) const; const TJCHAR* dump_string() const; @@ -960,6 +961,7 @@ class TJDictionary; /// /// unsigned int get_number_of_elements() const; + bool is_numeric(bool strict = false) const override; /// /// Try and get a string value, if it does not exist, then we return null. @@ -1551,6 +1553,7 @@ class TJDictionary; TJValue* element_at(int idx) const; bool is_array() const override; + bool is_numeric(bool strict = false) const override; void add(const TJValue* value); void add_boolean(bool value); @@ -1690,6 +1693,7 @@ class TJDictionary; virtual ~TJValueString(); bool is_string() const override; + bool is_numeric(bool strict = false) const override; const TJCHAR* raw_value() const; @@ -1727,6 +1731,7 @@ class TJDictionary; bool is_true() const override; bool is_false() const override; + bool is_numeric(bool strict = false) const override; protected: /// @@ -1752,6 +1757,7 @@ class TJDictionary; virtual ~TJValueNull() = default; bool is_null() const override; + bool is_numeric(bool strict = false) const override; protected: /// @@ -1775,6 +1781,7 @@ class TJDictionary; public: bool is_number() const override; + bool is_numeric(bool strict = false) const override; long double get_float() const; long long get_number() const; @@ -1882,6 +1889,7 @@ class TJDictionary; virtual ~TJValueComment(); bool is_comment() const override; + bool is_numeric(bool strict = false) const override; const TJCHAR* raw_value() const; diff --git a/tests/testtinyjsonis_numeric.cpp b/tests/testtinyjsonis_numeric.cpp new file mode 100644 index 0000000..608bfe1 --- /dev/null +++ b/tests/testtinyjsonis_numeric.cpp @@ -0,0 +1,80 @@ +// Licensed to Florent Guelfucci under one or more agreements. +// Florent Guelfucci licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#include +#define TJ_USE_CHAR 1 +#include "../src/TinyJSON.h" + +using namespace TinyJSON; + +TEST(TestIsNumeric, NumericValue) { + auto* num = TJ::parse("123"); + ASSERT_NE(nullptr, num); + EXPECT_TRUE(num->is_numeric(false)); + EXPECT_TRUE(num->is_numeric(true)); + delete num; +} + +TEST(TestIsNumeric, NumericString) { + auto* num_str = TJ::parse("\"123\""); + ASSERT_NE(nullptr, num_str); + EXPECT_TRUE(num_str->is_numeric(false)); + EXPECT_FALSE(num_str->is_numeric(true)); + delete num_str; +} + +TEST(TestIsNumeric, FloatNumericString) { + auto* float_str = TJ::parse("\"12.34\""); + ASSERT_NE(nullptr, float_str); + EXPECT_TRUE(float_str->is_numeric(false)); + EXPECT_FALSE(float_str->is_numeric(true)); + delete float_str; +} + +TEST(TestIsNumeric, ScientificNumericString) { + auto* sci_str = TJ::parse("\"-0.5e10\""); + ASSERT_NE(nullptr, sci_str); + EXPECT_TRUE(sci_str->is_numeric(false)); + EXPECT_FALSE(sci_str->is_numeric(true)); + delete sci_str; +} + +TEST(TestIsNumeric, NonNumericString) { + auto* alpha_str = TJ::parse("\"abc\""); + ASSERT_NE(nullptr, alpha_str); + EXPECT_FALSE(alpha_str->is_numeric(false)); + EXPECT_FALSE(alpha_str->is_numeric(true)); + delete alpha_str; +} + +TEST(TestIsNumeric, BooleanValue) { + auto* boolean = TJ::parse("true"); + ASSERT_NE(nullptr, boolean); + EXPECT_FALSE(boolean->is_numeric(false)); + EXPECT_FALSE(boolean->is_numeric(true)); + delete boolean; +} + +TEST(TestIsNumeric, NullValue) { + auto* nul = TJ::parse("null"); + ASSERT_NE(nullptr, nul); + EXPECT_FALSE(nul->is_numeric(false)); + EXPECT_FALSE(nul->is_numeric(true)); + delete nul; +} + +TEST(TestIsNumeric, ObjectValue) { + auto* obj = TJ::parse("{}"); + ASSERT_NE(nullptr, obj); + EXPECT_FALSE(obj->is_numeric(false)); + EXPECT_FALSE(obj->is_numeric(true)); + delete obj; +} + +TEST(TestIsNumeric, ArrayValue) { + auto* arr = TJ::parse("[]"); + ASSERT_NE(nullptr, arr); + EXPECT_FALSE(arr->is_numeric(false)); + EXPECT_FALSE(arr->is_numeric(true)); + delete arr; +}