MXS-1220: Add JSON Pointer support for json_t objects

Using the JSON Pointer syntax specified in RFC 6901
(https://tools.ietf.org/html/rfc6901) allows for a convenient way to
access values deep in a JSON object.
This commit is contained in:
Markus Mäkelä 2017-05-04 11:00:25 +03:00
parent 432a6d6f28
commit 2f8db4ec1a
4 changed files with 379 additions and 0 deletions

View File

@ -61,4 +61,13 @@ json_t* mxs_json_relationship(const char* host, const char* endpoint);
*/
void mxs_json_add_relation(json_t* rel, const char* id, const char* type);
/**
* @brief Return value at provided JSON Pointer
*
* @param json JSON object
* @param json_ptr JSON Pointer to object
* @return Pointed value or NULL if no value is found
*/
json_t* mxs_json_pointer(json_t* json, const char* json_ptr);
MXS_END_DECLS

View File

@ -61,3 +61,73 @@ void mxs_json_add_relation(json_t* rel, const char* id, const char* type)
json_object_set_new(obj, CN_TYPE, json_string(type));
json_array_append(data, obj);
}
static string grab_next_component(string* s)
{
std::string& str = *s;
while (str.length() > 0 && str[0] == '/')
{
str.erase(str.begin());
}
size_t pos = str.find("/");
string rval;
if (pos != string::npos)
{
rval = str.substr(0, pos);
str.erase(0, pos);
return rval;
}
else
{
rval = str;
str.erase(0);
}
return rval;
}
static bool is_integer(const string& str)
{
char* end;
return strtol(str.c_str(), &end, 10) >= 0 && *end == '\0';
}
static json_t* mxs_json_pointer_internal(json_t* json, string str)
{
json_t* rval = NULL;
string comp = grab_next_component(&str);
if (comp.length() == 0)
{
return json;
}
if (json_is_array(json) && is_integer(comp))
{
size_t idx = strtol(comp.c_str(), NULL, 10);
if (idx < json_array_size(json))
{
rval = mxs_json_pointer_internal(json_array_get(json, idx), str);
}
}
else if (json_is_object(json))
{
json_t* obj = json_object_get(json, comp.c_str());
if (obj)
{
rval = mxs_json_pointer_internal(obj, str);
}
}
return rval;
}
json_t* mxs_json_pointer(json_t* json, const char* json_ptr)
{
return mxs_json_pointer_internal(json, json_ptr);
}

View File

@ -23,6 +23,7 @@ add_executable(testmaxscalepcre2 testmaxscalepcre2.cc)
add_executable(testmodulecmd testmodulecmd.cc)
add_executable(testconfig testconfig.cc)
add_executable(trxboundaryparser_profile trxboundaryparser_profile.cc)
add_executable(testjson testjson.cc)
target_link_libraries(test_atomic maxscale-common)
target_link_libraries(test_adminusers maxscale-common)
target_link_libraries(test_buffer maxscale-common)
@ -48,6 +49,7 @@ target_link_libraries(testmaxscalepcre2 maxscale-common)
target_link_libraries(testmodulecmd maxscale-common)
target_link_libraries(testconfig maxscale-common)
target_link_libraries(trxboundaryparser_profile maxscale-common)
target_link_libraries(testjson maxscale-common)
add_test(TestAtomic test_atomic)
add_test(TestAdminUsers test_adminusers)
add_test(TestBuffer test_buffer)
@ -79,6 +81,7 @@ add_test(TestTrxCompare_Select test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../..
add_test(TestTrxCompare_Set test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/set.test)
add_test(TestTrxCompare_Update test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/update.test)
add_test(TestTrxCompare_MaxScale test_trxcompare ${CMAKE_CURRENT_SOURCE_DIR}/../../../query_classifier/test/maxscale.test)
add_test(TestJson testjson)
# This test requires external dependencies and thus cannot be run
# as a part of the core test set

View File

@ -0,0 +1,297 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <maxscale/cppdefs.hh>
#include <string>
#include <maxscale/debug.h>
#include <maxscale/jansson.hh>
#include <maxscale/json_api.h>
using std::string;
const char* test1_json =
"{"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/servers/\""
" },"
" \"data\": ["
" {"
" \"id\": \"server1\","
" \"type\": \"servers\","
" \"relationships\": {"
" \"services\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/services/\""
" },"
" \"data\": ["
" {"
" \"id\": \"RW-Split-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"SchemaRouter-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"RW-Split-Hint-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"Read-Connection-Router\","
" \"type\": \"services\""
" }"
" ]"
" },"
" \"monitors\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/monitors/\""
" },"
" \"data\": ["
" {"
" \"id\": \"MySQL-Monitor\","
" \"type\": \"monitors\""
" }"
" ]"
" }"
" },"
" \"attributes\": {"
" \"parameters\": {"
" \"address\": \"127.0.0.1\","
" \"port\": 3000,"
" \"protocol\": \"MySQLBackend\""
" },"
" \"status\": \"Master, Running\","
" \"version_string\": \"10.1.19-MariaDB-1~jessie\","
" \"node_id\": 3000,"
" \"master_id\": -1,"
" \"replication_depth\": 0,"
" \"slaves\": ["
" 3001,"
" 3002,"
" 3003"
" ],"
" \"statictics\": {"
" \"connections\": 0,"
" \"total_connections\": 0,"
" \"active_operations\": 0"
" }"
" }"
" },"
" {"
" \"id\": \"server2\","
" \"type\": \"servers\","
" \"relationships\": {"
" \"services\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/services/\""
" },"
" \"data\": ["
" {"
" \"id\": \"RW-Split-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"SchemaRouter-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"RW-Split-Hint-Router\","
" \"type\": \"services\""
" }"
" ]"
" },"
" \"monitors\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/monitors/\""
" },"
" \"data\": ["
" {"
" \"id\": \"MySQL-Monitor\","
" \"type\": \"monitors\""
" }"
" ]"
" }"
" },"
" \"attributes\": {"
" \"parameters\": {"
" \"address\": \"127.0.0.1\","
" \"port\": 3001,"
" \"protocol\": \"MySQLBackend\""
" },"
" \"status\": \"Slave, Running\","
" \"version_string\": \"10.1.19-MariaDB-1~jessie\","
" \"node_id\": 3001,"
" \"master_id\": 3000,"
" \"replication_depth\": 1,"
" \"slaves\": [],"
" \"statictics\": {"
" \"connections\": 0,"
" \"total_connections\": 0,"
" \"active_operations\": 0"
" }"
" }"
" },"
" {"
" \"id\": \"server3\","
" \"type\": \"servers\","
" \"relationships\": {"
" \"services\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/services/\""
" },"
" \"data\": ["
" {"
" \"id\": \"RW-Split-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"SchemaRouter-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"RW-Split-Hint-Router\","
" \"type\": \"services\""
" }"
" ]"
" },"
" \"monitors\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/monitors/\""
" },"
" \"data\": ["
" {"
" \"id\": \"MySQL-Monitor\","
" \"type\": \"monitors\""
" }"
" ]"
" }"
" },"
" \"attributes\": {"
" \"parameters\": {"
" \"address\": \"127.0.0.1\","
" \"port\": 3002,"
" \"protocol\": \"MySQLBackend\""
" },"
" \"status\": \"Slave, Running\","
" \"version_string\": \"10.1.19-MariaDB-1~jessie\","
" \"node_id\": 3002,"
" \"master_id\": 3000,"
" \"replication_depth\": 1,"
" \"slaves\": [],"
" \"statictics\": {"
" \"connections\": 0,"
" \"total_connections\": 0,"
" \"active_operations\": 0"
" }"
" }"
" },"
" {"
" \"id\": \"server4\","
" \"type\": \"servers\","
" \"relationships\": {"
" \"services\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/services/\""
" },"
" \"data\": ["
" {"
" \"id\": \"RW-Split-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"SchemaRouter-Router\","
" \"type\": \"services\""
" },"
" {"
" \"id\": \"RW-Split-Hint-Router\","
" \"type\": \"services\""
" }"
" ]"
" },"
" \"monitors\": {"
" \"links\": {"
" \"self\": \"http://localhost:8989/v1/monitors/\""
" },"
" \"data\": ["
" {"
" \"id\": \"MySQL-Monitor\","
" \"type\": \"monitors\""
" }"
" ]"
" }"
" },"
" \"attributes\": {"
" \"parameters\": {"
" \"address\": \"127.0.0.1\","
" \"port\": 3003,"
" \"protocol\": \"MySQLBackend\""
" },"
" \"status\": \"Slave, Running\","
" \"version_string\": \"10.1.19-MariaDB-1~jessie\","
" \"node_id\": 3003,"
" \"master_id\": 3000,"
" \"replication_depth\": 1,"
" \"slaves\": [],"
" \"statictics\": {"
" \"connections\": 0,"
" \"total_connections\": 0,"
" \"active_operations\": 0"
" }"
" }"
" }"
" ]"
"}";
int test1()
{
json_error_t err = {};
json_t* json = json_loads(test1_json, 0, &err);
ss_dassert(json);
ss_dassert(mxs_json_pointer(json, "") == json);
ss_dassert(mxs_json_pointer(json, "links") == json_object_get(json, "links"));
ss_dassert(json_is_string(mxs_json_pointer(json, "links/self")));
ss_dassert(mxs_json_pointer(json, "data") == json_object_get(json, "data"));
ss_dassert(json_is_array(mxs_json_pointer(json, "data")));
ss_dassert(json_is_object(mxs_json_pointer(json, "data/0")));
ss_dassert(json_is_string(mxs_json_pointer(json, "data/0/id")));
string s = json_string_value(mxs_json_pointer(json, "data/0/id"));
ss_dassert(s == "server1");
ss_dassert(json_is_object(mxs_json_pointer(json, "data/1")));
ss_dassert(json_is_string(mxs_json_pointer(json, "data/1/id")));
s = json_string_value(mxs_json_pointer(json, "data/1/id"));
ss_dassert(s == "server2");
ss_dassert(json_is_object(mxs_json_pointer(json, "data/0/attributes")));
ss_dassert(json_is_object(mxs_json_pointer(json, "data/0/attributes/parameters")));
ss_dassert(json_is_integer(mxs_json_pointer(json, "data/0/attributes/parameters/port")));
int i = json_integer_value(mxs_json_pointer(json, "data/0/attributes/parameters/port"));
ss_dassert(i == 3000);
ss_dassert(json_is_array(mxs_json_pointer(json, "data/0/attributes/slaves")));
ss_dassert(json_array_size(mxs_json_pointer(json, "data/0/attributes/slaves")) == 3);
return 0;
}
int main(int argc, char** argv)
{
test1();
return 0;
}