Skip to content

Commit 559471a

Browse files
committed
JSON: streamline API and fix bool/int output
1 parent 317c4a1 commit 559471a

File tree

6 files changed

+101
-80
lines changed

6 files changed

+101
-80
lines changed

core/src/JSON.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ std::string_view JsonGetStr(std::string_view json, std::string_view key)
5151
#ifdef ZXING_USE_CTRE
5252
for (auto [ma, mk, mv] : ctre::search_all<PATTERN>(json))
5353
if (IsEqualCaseInsensitive(key, mk))
54-
return mv;
54+
return mv.size() ? mv.to_view() : std::string_view(mk.data(), 0);
5555

5656
return {};
5757
#else
@@ -76,19 +76,4 @@ std::string_view JsonGetStr(std::string_view json, std::string_view key)
7676
#endif
7777
}
7878

79-
bool JsonGetBool(std::string_view json, std::string_view key)
80-
{
81-
#ifdef ZXING_USE_CTRE
82-
for (auto [ma, mk, mv] : ctre::search_all<PATTERN>(json))
83-
if (IsEqualCaseInsensitive(key, mk))
84-
return mv.size() == 0 || Contains("1tT", *mv.data());
85-
86-
return false;
87-
#else
88-
auto val = JsonGetStr(json, key);
89-
90-
return val.data() && (val.size() == 0 || Contains("1tT", val.front()));
91-
#endif
92-
}
93-
9479
} // ZXing

core/src/JSON.h

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,50 @@
55

66
#pragma once
77

8-
#include "Error.h"
8+
#include "ZXAlgorithms.h"
99

10-
#include <charconv>
1110
#include <cstring>
12-
#include <stdexcept>
11+
#include <optional>
1312
#include <string>
1413
#include <string_view>
1514

1615
namespace ZXing {
1716

18-
inline std::string JsonValue(std::string_view key, std::string_view val, int indent = 0)
17+
inline std::string JsonKeyValue(std::string_view key, std::string_view val)
1918
{
20-
//TODO: use std::format from c++20
21-
return val.empty() ? std::string() : std::string(indent * 2, ' ') + "\"" + std::string(key) + "\":\"" + std::string(val) + "\",";
19+
return val.empty() ? std::string() : StrCat("\"", key, "\":", val, ',');
2220
}
2321

24-
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
25-
inline std::string JsonValue(std::string_view key, T val, int indent = 0)
22+
template<typename T>
23+
inline std::string JsonValue(std::string_view key, T val)
2624
{
2725
if constexpr (std::is_same_v<T, bool>)
28-
return JsonValue(key, val ? "true" : "", indent);
26+
return val ? JsonKeyValue(key, "true") : "";
27+
else if constexpr (std::is_arithmetic_v<T>)
28+
return JsonKeyValue(key, std::to_string(val));
29+
else if constexpr (std::is_convertible_v<T, std::string_view>)
30+
return JsonKeyValue(key, StrCat("\"" , val, "\""));
2931
else
30-
return JsonValue(key, std::to_string(val), indent);
32+
static_assert("unsupported JSON value type");
3133
}
3234

33-
bool JsonGetBool(std::string_view json, std::string_view key);
3435
std::string_view JsonGetStr(std::string_view json, std::string_view key);
3536

3637
template <typename T>
37-
inline T JsonGet(std::string_view json, std::string_view key)
38+
inline std::optional<T> JsonGet(std::string_view json, std::string_view key)
3839
{
39-
if constexpr (std::is_same_v<bool, T>)
40-
return JsonGetBool(json, key);
41-
if constexpr (std::is_same_v<std::string_view, T>)
42-
return JsonGetStr(json, key);
43-
44-
throw UnsupportedError("internal error");
45-
}
40+
auto str = JsonGetStr(json, key);
41+
if (!str.data())
42+
return std::nullopt;
4643

47-
inline int svtoi(std::string_view sv)
48-
{
49-
int val = 0;
50-
auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), val);
51-
if (ec != std::errc() || ptr != sv.data() + sv.size())
52-
throw std::invalid_argument("failed to parse int from '" + std::string(sv) + "'");
53-
54-
return val;
44+
if constexpr (std::is_same_v<bool, T>)
45+
return str.empty() || Contains("1tT", str.front()) ? std::optional(true) : std::nullopt;
46+
else if constexpr (std::is_arithmetic_v<T>)
47+
return str.empty() ? std::nullopt : std::optional(FromString<T>(str));
48+
else if constexpr (std::is_same_v<std::string_view, T>)
49+
return str;
50+
else
51+
static_assert("unsupported JSON value type");
5552
}
5653

5754
} // ZXing

core/src/WriteBarcode.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ ZX_PROPERTY(std::string, options)
6262
#undef ZX_PROPERTY
6363

6464
#define ZX_RO_PROPERTY(TYPE, NAME) \
65-
TYPE CreatorOptions::NAME() const noexcept { return JsonGet<TYPE>(d->options, #NAME); }
65+
std::optional<TYPE> CreatorOptions::NAME() const noexcept { return JsonGet<TYPE>(d->options, #NAME); }
6666

6767
ZX_RO_PROPERTY(bool, gs1);
6868
ZX_RO_PROPERTY(bool, stacked);
69-
ZX_RO_PROPERTY(std::string_view, version);
70-
ZX_RO_PROPERTY(std::string_view, dataMask);
69+
ZX_RO_PROPERTY(int, version);
70+
ZX_RO_PROPERTY(int, dataMask);
7171

7272
#undef ZX_RO_PROPERTY
7373

@@ -371,11 +371,11 @@ zint_symbol* CreatorOptions::zint() const
371371
if (!ecLevel().empty())
372372
zint->option_1 = ParseECLevel(zint->symbology, ecLevel());
373373

374-
if (auto str = version(); str.size() && !IsLinearBarcode(format()))
375-
zint->option_2 = svtoi(str);
374+
if (auto val = version(); val && !IsLinearBarcode(format()))
375+
zint->option_2 = *val;
376376

377-
if (auto str = dataMask(); str.size() && (BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode).testFlag(format()))
378-
zint->option_3 = (zint->option_3 & 0xFF) | (svtoi(str) + 1) << 8;
377+
if (auto val = dataMask(); val && (BarcodeFormat::QRCode | BarcodeFormat::MicroQRCode).testFlag(format()))
378+
zint->option_3 = (zint->option_3 & 0xFF) | (*val + 1) << 8;
379379
}
380380

381381
return zint.get();

core/src/WriteBarcode.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "ImageView.h"
1212

1313
#include <memory>
14+
#include <optional>
1415
#include <string_view>
1516

1617
extern "C" struct zint_symbol;
@@ -48,12 +49,12 @@ class CreatorOptions
4849
#undef ZX_PROPERTY
4950

5051
#define ZX_RO_PROPERTY(TYPE, NAME) \
51-
TYPE NAME() const noexcept;
52+
std::optional<TYPE> NAME() const noexcept;
5253

5354
ZX_RO_PROPERTY(bool, gs1);
5455
ZX_RO_PROPERTY(bool, stacked);
55-
ZX_RO_PROPERTY(std::string_view, version);
56-
ZX_RO_PROPERTY(std::string_view, dataMask);
56+
ZX_RO_PROPERTY(int, version);
57+
ZX_RO_PROPERTY(int, dataMask);
5758
#undef ZX_RO_PROPERTY
5859
};
5960

core/src/ZXAlgorithms.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
#include "Error.h"
99

1010
#include <algorithm>
11+
#include <charconv>
1112
#include <cstring>
1213
#include <initializer_list>
1314
#include <iterator>
1415
#include <numeric>
1516
#include <string>
17+
#include <stdexcept>
1618
#include <utility>
1719

1820
namespace ZXing {
@@ -136,6 +138,32 @@ std::string ToString(T val, int len)
136138
return result;
137139
}
138140

141+
template <class T>
142+
constexpr std::string_view TypeName()
143+
{
144+
#ifdef __clang__
145+
std::string_view p = __PRETTY_FUNCTION__;
146+
return p.substr(40, p.size() - 40 - 1);
147+
#elif defined(__GNUC__)
148+
std::string_view p = __PRETTY_FUNCTION__;
149+
return p.substr(55, p.find(';', 55) - 55);
150+
#elif defined(_MSC_VER)
151+
std::string_view p = __FUNCSIG__;
152+
return p.substr(90, p.size() - 90 - 7);
153+
#endif
154+
}
155+
156+
template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
157+
inline T FromString(std::string_view sv)
158+
{
159+
T val = {};
160+
auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), val);
161+
if (ec != std::errc() || ptr != sv.data() + sv.size())
162+
throw std::invalid_argument(StrCat("failed to parse '", TypeName<T>(), "' from '", sv, "'"));
163+
164+
return val;
165+
}
166+
139167
template <typename T>
140168
void UpdateMin(T& min, T val)
141169
{

test/unit/JSONTest.cpp

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,8 @@ using namespace ZXing;
1212
TEST(JSONTest, Value)
1313
{
1414
EXPECT_EQ(JsonValue("key", "val"), R"("key":"val",)");
15-
EXPECT_EQ(JsonValue("key", true), R"("key":"true",)");
16-
EXPECT_EQ(JsonValue("key", 1), R"("key":"1",)");
17-
}
18-
19-
TEST(JSONTest, GetBool)
20-
{
21-
EXPECT_TRUE(JsonGetBool("key", "key"));
22-
EXPECT_TRUE(JsonGetBool("key:true", "key"));
23-
EXPECT_TRUE(JsonGetBool("key:1", "key"));
24-
EXPECT_TRUE(JsonGetBool("key,other", "key"));
25-
EXPECT_TRUE(JsonGetBool("key", "KEY"));
26-
EXPECT_TRUE(JsonGetBool("key1", "key1"));
27-
28-
EXPECT_FALSE(JsonGetBool("", ""));
29-
EXPECT_FALSE(JsonGetBool("", "key"));
30-
EXPECT_FALSE(JsonGetBool("key:", "key"));
31-
EXPECT_FALSE(JsonGetBool("key:false", "key"));
32-
EXPECT_FALSE(JsonGetBool("key:0", "key"));
33-
EXPECT_FALSE(JsonGetBool("keys", "key"));
34-
EXPECT_FALSE(JsonGetBool("thekey", "key"));
35-
36-
EXPECT_TRUE(JsonGetBool("key , other", "key"));
37-
EXPECT_TRUE(JsonGetBool("\"key\": \"true\"", "key"));
38-
EXPECT_TRUE(JsonGetBool("{\"key\": \"true\"}", "key")); // JSON
39-
EXPECT_TRUE(JsonGetBool("{'key': True'}", "key")); // Python
15+
EXPECT_EQ(JsonValue("key", true), R"("key":true,)");
16+
EXPECT_EQ(JsonValue("key", 1), R"("key":1,)");
4017
}
4118

4219
TEST(JSONTest, GetStr)
@@ -51,6 +28,39 @@ TEST(JSONTest, GetStr)
5128
EXPECT_EQ(JsonGetStr("key:abc", "KEY"), "abc");
5229

5330
EXPECT_EQ(JsonGetStr("\"key\": \"abc\"", "KEY"), "abc");
54-
EXPECT_EQ(JsonGetStr("{\"key\": \"true\"}", "key"), "true"); // JSON
55-
EXPECT_EQ(JsonGetStr("{'key': True}", "key"), "True"); // Python
31+
EXPECT_EQ(JsonGetStr("{\"key\": true}", "key"), "true"); // JSON
32+
EXPECT_EQ(JsonGetStr("{'key': True}", "key"), "True"); // Python
33+
}
34+
35+
TEST(JSONTest, GetBool)
36+
{
37+
EXPECT_TRUE(JsonGet<bool>("key", "key"));
38+
EXPECT_TRUE(JsonGet<bool>("key:true", "key"));
39+
EXPECT_TRUE(JsonGet<bool>("key:1", "key"));
40+
EXPECT_TRUE(JsonGet<bool>("key,other", "key"));
41+
EXPECT_TRUE(JsonGet<bool>("key", "KEY"));
42+
EXPECT_TRUE(JsonGet<bool>("key1", "key1"));
43+
44+
EXPECT_FALSE(JsonGet<bool>("", ""));
45+
EXPECT_FALSE(JsonGet<bool>("", "key"));
46+
EXPECT_FALSE(JsonGet<bool>("key:", "key"));
47+
EXPECT_FALSE(JsonGet<bool>("key:false", "key"));
48+
EXPECT_FALSE(JsonGet<bool>("key:0", "key"));
49+
EXPECT_FALSE(JsonGet<bool>("keys", "key"));
50+
EXPECT_FALSE(JsonGet<bool>("thekey", "key"));
51+
52+
EXPECT_TRUE(JsonGet<bool>("key , other", "key"));
53+
EXPECT_TRUE(JsonGet<bool>("\"key\": \"true\"", "key"));
54+
EXPECT_TRUE(JsonGet<bool>("{\"key\": true}", "key")); // JSON
55+
EXPECT_TRUE(JsonGet<bool>("{'key': True'}", "key")); // Python
56+
}
57+
58+
TEST(JSONTest, GetInt)
59+
{
60+
EXPECT_FALSE(JsonGet<int>("key:", "key"));
61+
EXPECT_THROW(JsonGet<int>("key:false", "key"), std::invalid_argument);
62+
63+
EXPECT_EQ(JsonGet<int>("key:1", "key").value(), 1);
64+
EXPECT_EQ(JsonGet<int>("{\"key\": 2}", "key").value(), 2); // JSON
65+
EXPECT_EQ(JsonGet<int>("{'key': 1}", "key").value(), 1); // Python
5666
}

0 commit comments

Comments
 (0)