json.cpp, json.h: Allow to use any string for object keys.

Convert to final key with new public function 'json::str2key()'.
Rename related variables from 'key' to 'keystr'.

git-svn-id: http://svn.code.sf.net/p/smartmontools/code/trunk@5292 4ea69e1a-61f1-4043-bf83-b5c94c648137
pull/86/merge
chrfranke 2022-01-06 17:13:25 +00:00
parent dc940da30c
commit f3043a8edf
3 changed files with 66 additions and 47 deletions

View File

@ -1,5 +1,10 @@
$Id$
2022-01-06 Christian Franke <franke@computer.org>
json.cpp, json.h: Allow to use any string for object keys.
Convert to final key with new public function 'json::str2key()'.
Rename related variables from 'key' to 'keystr'.
2022-01-06 Alex Samorukov <samm@os2.kiev.ua>

View File

@ -3,7 +3,7 @@
*
* Home page of code is: https://www.smartmontools.org
*
* Copyright (C) 2017-21 Christian Franke
* Copyright (C) 2017-22 Christian Franke
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -32,13 +32,18 @@ static void jassert_failed(int line, const char * expr)
#define jassert(expr) (!(expr) ? jassert_failed(__LINE__, #expr) : (void)0)
static void check_key(const char * key)
std::string json::str2key(const char * str)
{
// Limit: object keys should be valid identifiers (lowercase only)
char c = key[0];
jassert('a' <= c && c <= 'z');
for (int i = 1; (c = key[i]); i++)
jassert(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || (c == '_'));
std::string key = str;
for (char & c : key) {
if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || c == '_')
continue;
if ('A' <= c && c <= 'Z')
c += 'a' - 'A';
else
c = '_';
}
return key;
}
json::ref::ref(json & js)
@ -46,18 +51,18 @@ json::ref::ref(json & js)
{
}
json::ref::ref(json & js, const char * key)
json::ref::ref(json & js, const char * keystr)
: m_js(js)
{
check_key(key);
m_path.push_back(node_info(key));
jassert(keystr && *keystr);
m_path.push_back(node_info(keystr));
}
json::ref::ref(const ref & base, const char * key)
json::ref::ref(const ref & base, const char * keystr)
: m_js(base.m_js), m_path(base.m_path)
{
check_key(key);
m_path.push_back(node_info(key));
jassert(keystr && *keystr);
m_path.push_back(node_info(keystr));
}
json::ref::ref(const ref & base, int index)
@ -206,11 +211,11 @@ void json::ref::set_unsafe_le128(const void * pvalue)
void json::ref::operator+=(std::initializer_list<initlist_key_value_pair> ilist)
{
for (const initlist_key_value_pair & kv : ilist) {
jassert(kv.key && *kv.key);
jassert(kv.keystr && *kv.keystr);
switch (kv.value.type) {
default: operator[](kv.key) = kv.value; break;
case nt_object: operator[](kv.key) += kv.object; break;
case nt_array: operator[](kv.key) += kv.array; break;
default: operator[](kv.keystr) = kv.value; break;
case nt_object: operator[](kv.keystr) += kv.object; break;
case nt_array: operator[](kv.keystr) += kv.array; break;
}
}
}

View File

@ -22,25 +22,20 @@
/// Create and print JSON output.
class json
{
private:
struct node_info
{
std::string key;
int index = 0;
node_info() = default;
explicit node_info(const char * key_) : key(key_) { }
explicit node_info(int index_) : index(index_) { }
};
typedef std::vector<node_info> node_path;
public:
/// Return true if value is a safe JSON integer.
/// Same as Number.isSafeInteger(value) in JavaScript.
static bool is_safe_uint(unsigned long long value)
{ return (value < (1ULL << 53)); }
/// Replace space and non-alphanumerics with '_', upper to lower case.
static std::string str2key(const char * str);
/// Replace space and non-alphanumerics with '_', upper to lower case
/// (std::string variant).
static std::string str2key(const std::string & str)
{ return str2key(str.c_str()); }
enum node_type {
nt_unset, nt_object, nt_array,
nt_bool, nt_int, nt_uint, nt_uint128, nt_string
@ -74,23 +69,37 @@ public:
};
struct initlist_key_value_pair {
initlist_key_value_pair(const char * k, const initlist_value & v) : key(k), value(v) {}
initlist_key_value_pair(const char * k, const initlist_value & v) : keystr(k), value(v) {}
initlist_key_value_pair(const std::string & k, const initlist_value & v)
: key(k.c_str()), value(v) {}
: keystr(k.c_str()), value(v) {}
initlist_key_value_pair(const char * k, const std::initializer_list<initlist_key_value_pair> & ilist)
: key(k), value(nt_object), object(ilist) {}
: keystr(k), value(nt_object), object(ilist) {}
initlist_key_value_pair(const std::string & k, const std::initializer_list<initlist_key_value_pair> & ilist)
: key(k.c_str()), value(nt_object), object(ilist) {}
: keystr(k.c_str()), value(nt_object), object(ilist) {}
initlist_key_value_pair(const char * k, const std::initializer_list<initlist_value> & ilist)
: key(k), value(nt_array), array(ilist) {}
: keystr(k), value(nt_array), array(ilist) {}
initlist_key_value_pair(const std::string & k, const std::initializer_list<initlist_value> & ilist)
: key(k.c_str()), value(nt_array), array(ilist) {}
const char * key;
: keystr(k.c_str()), value(nt_array), array(ilist) {}
const char * keystr;
initlist_value value;
std::initializer_list<initlist_key_value_pair> object;
std::initializer_list<initlist_value> array;
};
private:
struct node_info
{
std::string key;
int index = 0;
node_info() = default;
explicit node_info(const char * keystr) : key(str2key(keystr)) { }
explicit node_info(int index_) : index(index_) { }
};
typedef std::vector<node_info> node_path;
public:
/// Reference to a JSON element.
class ref
{
@ -98,12 +107,12 @@ public:
~ref();
/// Return reference to object element.
ref operator[](const char * key) const
{ return ref(*this, key); }
ref operator[](const char * keystr) const
{ return ref(*this, keystr); }
/// Return reference to object element (std::string variant).
ref operator[](const std::string & key) const
{ return ref(*this, key.c_str()); }
ref operator[](const std::string & keystr) const
{ return ref(*this, keystr.c_str()); }
/// Return reference to array element.
ref operator[](int index) const
@ -146,8 +155,8 @@ public:
private:
friend class json;
explicit ref(json & js);
ref(json & js, const char * key);
ref(const ref & base, const char * key);
ref(json & js, const char * keystr);
ref(const ref & base, const char * keystr);
ref(const ref & base, int index);
ref(const ref & base, const char * /*dummy*/, const char * key_suffix);
@ -159,12 +168,12 @@ public:
};
/// Return reference to element of top level object.
ref operator[](const char * key)
{ return ref(*this, key); }
ref operator[](const char * keystr)
{ return ref(*this, keystr); }
/// Return reference to element of top level object (std::string variant).
ref operator[](const std::string & key)
{ return ref(*this, key.c_str()); }
ref operator[](const std::string & keystr)
{ return ref(*this, keystr.c_str()); }
/// Braced-init-list support for top level object.
void operator+=(std::initializer_list<initlist_key_value_pair> ilist)