From 1a2ac0075274598fcbf02c3cc6aa05e67138080b Mon Sep 17 00:00:00 2001 From: Jesse Gilles Date: Thu, 30 Apr 2015 16:46:24 -0500 Subject: initial commit --- main.cpp | 486 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 main.cpp (limited to 'main.cpp') 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 . + * + */ + +#include +#include +#include +#include +#include +#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 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 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 ]\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 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; +} + -- cgit v1.2.3