summaryrefslogtreecommitdiff
path: root/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'main.cpp')
-rw-r--r--main.cpp486
1 files changed, 486 insertions, 0 deletions
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..41885c7
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2015 by Multi-Tech Systems
+ *
+ * This file is part of jsparser.
+ *
+ * jsparser is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * jsparser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with jsparser. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <mts/MTS_Text.h>
+#include <json/json.h>
+#include <sstream>
+#include <getopt.h>
+#include <iostream>
+#include "Version.h"
+
+//OPTIONS
+const uint32_t OPT_NONE = 0x00000000;
+const uint32_t OPT_JSOBJ = 0x00000001;
+const uint32_t OPT_COUNT = 0x00000002;
+const uint32_t OPT_LIST = 0x00000004;
+const uint32_t OPT_SEARCH = 0x00000008;
+
+const uint32_t OPT_ISNULL = 0x00000010;
+const uint32_t OPT_ISBOOL = 0x00000020;
+const uint32_t OPT_ISSTRING = 0x00000040;
+const uint32_t OPT_ISINT = 0x00000080;
+const uint32_t OPT_ISUINT = 0x00000100;
+const uint32_t OPT_ISFLOAT = 0x00000200;
+const uint32_t OPT_ISARRAY = 0x00000400;
+const uint32_t OPT_ISOBJECT = 0x00000800;
+
+const uint32_t OPT_BASICOPTIONS = 0x0000000F;
+const uint32_t OPT_ISOPTIONS = 0x00000FF0;
+
+uint32_t g_iOptions = 0;
+std::string g_sKey;
+std::string g_sValue;
+
+std::string get(const std::string& sPath, const Json::Value& jData);
+std::string cleansePath(const std::string& path);
+std::string parseOptions(int argc, char** argv);
+std::string handleSearchOption(const Json::Value& data);
+std::string handleIsOptions(const Json::Value& data);
+
+int main(int argc, char** argv) {
+
+ Json::Value jInput;
+ std::string sInput;
+ std::string sPath(parseOptions(argc, argv));
+
+ std::stringstream ss;
+ std::string line;
+ while (std::getline(std::cin, line)) {
+ ss << line << std::endl;
+ }
+
+ sInput = ss.str();
+
+ //printf("INPUT:(%s)\n", g_sInput.c_str());
+
+ Json::Features features = Json::Features::strictMode();
+ Json::Reader reader(features);
+
+ if (!reader.parse(sInput, jInput)) {
+ fprintf(stderr, "bad json input\n");
+ return 1;
+ }
+
+ printf("%s", get(sPath, jInput).c_str());
+
+ return 0;
+}
+
+std::string cleansePath(const std::string& path) {
+ std::string clean(path);
+
+ while (clean.size() > 0 && clean[0] == '/') {
+ clean = clean.substr(1);
+ }
+
+ while (clean.size() > 0 && clean[clean.size() - 1] == '/') {
+ clean = clean.substr(0, clean.size() - 1);
+ }
+
+ return clean;
+}
+
+std::string get(const std::string& sPath, const Json::Value& jData) {
+ std::vector<std::string> components = MTS::Text::split(sPath, '/');
+ const Json::Value* tmp = &jData;
+
+ for (uint32_t i = 0; i < components.size(); i++) {
+
+ if (tmp->isNull()) {
+ if (g_iOptions & OPT_ISNULL) {
+ printf("true");
+ } else {
+ fprintf(stderr, "null path [%s]. failed on [%s]\n", sPath.c_str(), components[i].c_str());
+ }
+ exit(1);
+ }
+
+ if (components[i].size() == 0) {
+ continue;
+ }
+
+ if (tmp->isArray()) {
+ uint32_t index;
+ if (MTS::Text::parse(index, components[i]) && tmp->isValidIndex(index)) {
+ tmp = &(*tmp)[index];
+ } else {
+ if (g_iOptions & OPT_ISNULL) {
+ printf("true");
+ } else {
+ fprintf(stderr, "array element is null. path [%s]. failed on [%s]\n", sPath.c_str(), components[i].c_str());
+ }
+ exit(1);
+ }
+ } else if (tmp->isObject() && tmp->isMember(components[i])) {
+ tmp = &(*tmp)[components[i]];
+ } else {
+ if (g_iOptions & OPT_ISNULL) {
+ printf("true");
+ } else {
+ if (g_iOptions & (OPT_ISOPTIONS)) {
+ printf("false");
+ } else {
+ fprintf(stderr, "not an object or array. null path [%s]. failed on [%s]\n", sPath.c_str(), components[i].c_str());
+ }
+ }
+ exit(1);
+ }
+ }
+
+ if (g_iOptions & OPT_SEARCH) {
+ return handleSearchOption(*tmp);
+ }
+
+ if (g_iOptions & OPT_ISOPTIONS) {
+ return handleIsOptions(*tmp);
+ }
+
+ if (g_iOptions & OPT_LIST) {
+ if (!tmp->isNull() && !tmp->isObject()) {
+ fprintf(stderr, "path must lead to an object to use --list option\n");
+ exit(1);
+ }
+ std::vector<std::string> list = tmp->getMemberNames();
+ std::string csv;
+ for (uint32_t i = 0; i < list.size() - 1; i++) {
+ csv.append(list[i]);
+ csv.append(",");
+ }
+ csv.append(list[list.size() - 1]);
+ return csv;
+ }
+
+ if (g_iOptions & OPT_COUNT) {
+ std::stringstream ss;
+ ss << tmp->size();
+ return ss.str();
+ }
+
+ if (tmp->isString()) {
+ return tmp->asString();
+ }
+
+ if (tmp->isBool()) {
+ if (tmp->asBool()) {
+ return "true";
+ } else {
+ return "false";
+ }
+ }
+ if (tmp->isDouble()) {
+ std::stringstream ss;
+ ss << tmp->asDouble();
+ return ss.str();
+ }
+ if (tmp->isInt()) {
+ std::stringstream ss;
+ ss << tmp->asInt();
+ return ss.str();
+ }
+ if (tmp->isUInt()) {
+ std::stringstream ss;
+ ss << tmp->asUInt();
+ return ss.str();
+ }
+
+ if (g_iOptions & OPT_JSOBJ) {
+ //Returning Json Object
+ return tmp->toStyledString();
+ }
+
+ fprintf(stderr, "path ends at json object. increase depth or add --jsobj option\n");
+ exit(1);
+}
+
+std::string handleSearchOption(const Json::Value& data) {
+ if (!data.isArray()) {
+ fprintf(stderr, "path does not lead to array\n");
+ return "-1";
+ }
+
+ uint32_t index;
+ for (index = 0; index < data.size(); index++) {
+ if (data[index].isArray() || data[index].isNull()) {
+ continue;
+ }
+ Json::Value value;
+ if (data[index].isObject() && data[index].isMember(g_sKey)) {
+ value = data[index][g_sKey];
+ } else {
+ value = data[index];
+ }
+
+ if (value.isString()) {
+ if (g_sValue == value.asString()) {
+ break;
+ }
+ }
+ if (value.isInt()) {
+ int64_t iValue;
+ if (MTS::Text::parse(iValue, g_sValue) && iValue == value.asInt()) {
+ break;
+ }
+ }
+ if (value.isUInt()) {
+ uint64_t iValue;
+ if (MTS::Text::parse(iValue, g_sValue) && iValue == value.asUInt()) {
+ break;
+ }
+ }
+ if (value.isDouble()) {
+ double fValue;
+ if (MTS::Text::parse(fValue, g_sValue) && fValue == value.asDouble()) {
+ break;
+ }
+ }
+ if (value.isBool()) {
+ if (g_sValue == "true" && value.asBool()) {
+ break;
+ }
+ if (g_sValue == "false" && !value.asBool()) {
+ break;
+ }
+ }
+ }
+
+ if (index != data.size()) {
+ return MTS::Text::format(index);
+ } else {
+ return "-1";
+ }
+}
+
+std::string handleIsOptions(const Json::Value& data) {
+ uint32_t type = 0;
+ if (data.isString()) {
+ type |= OPT_ISSTRING;
+ }
+
+ if (data.isBool()) {
+ type |= OPT_ISBOOL;
+ }
+ if (data.isDouble()) {
+ type |= OPT_ISFLOAT;
+ }
+ if (data.isInt()) {
+ type |= OPT_ISINT;
+ }
+ if (data.isUInt()) {
+ type |= OPT_ISUINT;
+ }
+
+ if (data.isNull()) {
+ type |= OPT_ISNULL;
+ }
+
+ if (data.isArray()) {
+ type |= (OPT_ISARRAY);
+ }
+
+ if (data.isObject()) {
+ type |= (OPT_ISOBJECT);
+ }
+
+ if ((g_iOptions & OPT_ISOPTIONS) & type) {
+ return "true";
+ }
+
+ return "false";
+}
+
+std::string parseOptions(int argc, char** argv) {
+ int c;
+ std::string sPath;
+
+ while (1) {
+ static struct option long_options[] =
+ { { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'v' },
+ { "jsobj", no_argument, 0, 'j' },
+ { "count", no_argument, 0, 'c' },
+ { "list", no_argument, 0, 'l' },
+ { "isNull", no_argument, 0, 'n' },
+ { "isBool", no_argument, 0, 'b' },
+ { "isString", no_argument, 0, 's' },
+ { "isInt", no_argument, 0, 'i' },
+ { "isUInt", no_argument, 0, 'u' },
+ { "isFloat", no_argument, 0, 'f' },
+ { "isArray", no_argument, 0, 'a' },
+ { "isObject", no_argument, 0, 'o' },
+ { "path", required_argument, 0, 'p' },
+ { "search", required_argument, 0, 'q' },
+ { 0, 0, 0, 0 } };
+
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "hvjclnbsiufaop:q:", long_options, &option_index);
+
+ /* Detect the end of the options. */
+ if (c == -1)
+ break;
+
+ switch (c) {
+
+ case 'h':
+ printf("Usage: %s [OPTIONS] [--path <path>]\n", argv[0]);
+ printf("\tAccepts JSON through stdin.\n");
+ printf("\tReturns element at end of path parameter.\n");
+ printf("\tOPTIONS:\n");
+ printf("\t--jsobj (j) : returns JSON object at the end of path\n");
+ printf("\t--path (p) : Specify path to nested json element\n");
+ printf("\t--count (c) : returns number of elements in an array or object\n");
+ printf("\t--list (l) : returns comma-delimited list of keys within an object\n");
+ printf("\t--search (q) : returns index of array that contains search criteria\n");
+ printf("\t--isNull (n) : returns 'true' if NULL, 'false' otherwise\n");
+ printf("\t--isBool (b) : returns 'true' if Boolean, 'false' otherwise\n");
+ printf("\t--isString (s) : returns 'true' if String, 'false' otherwise\n");
+ printf("\t--isInt (i) : returns 'true' if Integer, 'false' otherwise\n");
+ printf("\t--isUInt (u) : returns 'true' if Unsigned Integer, 'false' otherwise\n");
+ printf("\t--isFloat (f) : returns 'true' if Float or Double, 'false' otherwise\n");
+ printf("\t--isArray (a) : returns 'true' if JSON Array, 'false' otherwise\n");
+ printf("\t--isObject (o) : returns 'true' if JSON Object, 'false' otherwise\n");
+ printf("\t--version (v) : show version\n");
+ printf("\t--help (h) : show this help\n");
+ printf("\tWrites to stderr if bad path or json data.\n");
+ printf("\tPath uses backslash characters to specify nested json element.\n");
+ printf("\tUse integers to index an element of an array\n");
+ printf("\tExamples: \n");
+ printf("\t\t#>echo { \\\"result\\\" : { \\\"enabled\\\" : true } } | %s -p /result/enabled\n\t\t#>true\n", argv[0]);
+ printf("\t\t#>echo { \\\"result\\\" : [ \\\"bears\\\", \\\"beets\\\", \\\"battlestar galactica\\\" ] } | %s -p /result/0\n\t\t#>bears\n", argv[0]);
+ exit(0);
+ break;
+
+ case 'v':
+ printf("Version: %s\n", Version::version.c_str());
+ exit(0);
+ break;
+
+ case 'p':
+ sPath = cleansePath(optarg);
+ break;
+
+ case 'j':
+ g_iOptions |= OPT_JSOBJ;
+ break;
+
+ case 'c':
+ g_iOptions |= OPT_COUNT;
+ break;
+
+ case 'l':
+ g_iOptions |= OPT_LIST;
+ break;
+
+ case 'q':
+ {
+ g_iOptions |= OPT_SEARCH;
+ std::vector<std::string> kvp = MTS::Text::split(optarg, '=');
+ if (kvp.size() == 1) {
+ g_sValue = kvp[0];
+ } else if (kvp.size() == 2) {
+ g_sKey = kvp[0];
+ g_sValue = kvp[1];
+ } else {
+ fprintf(stderr, "search criteria must be a single value or key-value pair: value or key=value\n");
+ exit(1);
+ }
+ }
+ break;
+
+ case 'n':
+ g_iOptions |= OPT_ISNULL;
+ break;
+
+ case 's':
+ g_iOptions |= OPT_ISSTRING;
+ break;
+
+ case 'b':
+ g_iOptions |= OPT_ISBOOL;
+ break;
+
+ case 'i':
+ g_iOptions |= OPT_ISINT;
+ break;
+
+ case 'u':
+ g_iOptions |= OPT_ISUINT;
+ break;
+
+ case 'f':
+ g_iOptions |= OPT_ISFLOAT;
+ break;
+
+ case 'a':
+ g_iOptions |= (OPT_ISARRAY);
+ break;
+
+ case 'o':
+ g_iOptions |= (OPT_ISOBJECT);
+ break;
+
+ case '?':
+ /* getopt_long already printed an error message. */
+ break;
+
+ default:
+ printf("ABORTING!!\n");
+ abort();
+ break;
+ }
+ }
+
+ /* Print any remaining command line arguments (not options). */
+ if (optind < argc) {
+ fprintf(stderr, "non-option ARGV-elements: \n");
+ while (optind < argc)
+ fprintf(stderr, "%s ", argv[optind++]);
+ fprintf(stderr, "\n");
+ }
+
+ if ((g_iOptions & OPT_JSOBJ) && (g_iOptions & (~OPT_JSOBJ))) {
+ fprintf(stderr, "Option --jsobj can only be used with --path option\n");
+ exit(1);
+ }
+
+ if ((g_iOptions & OPT_COUNT) && (g_iOptions & (~OPT_COUNT))) {
+ fprintf(stderr, "Option --count can only be used with --path option\n");
+ exit(1);
+ }
+
+ if ((g_iOptions & OPT_LIST) && (g_iOptions & (~OPT_LIST))) {
+ fprintf(stderr, "Option --list can only be used with --path option\n");
+ exit(1);
+ }
+
+ if ((g_iOptions & OPT_SEARCH) && (g_iOptions & (~OPT_SEARCH))) {
+ fprintf(stderr, "Option --search can only be used with --path option\n");
+ exit(1);
+ }
+
+ if ((g_iOptions & OPT_ISOPTIONS) && (g_iOptions & (~OPT_ISOPTIONS))) {
+ fprintf(stderr, "Option \"--is*\" querries can only be used with --path option\n");
+ exit(1);
+ }
+
+ return sPath;
+}
+