/* * 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 sPath(parseOptions(argc, argv)); #if defined(JSONCPP_VERSION_HEXA) && (JSONCPP_VERSION_HEXA > 0x010600) // > 1.6.0 Json::CharReaderBuilder builder; builder.strictMode(&builder.settings_); if (!Json::parseFromStream(builder, std::cin, &jInput, NULL)) { fprintf(stderr, "bad json input\n"); return 1; } #else std::string sInput; 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; } #endif 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->isInt()) { std::stringstream ss; ss << tmp->asInt(); return ss.str(); } if (tmp->isUInt()) { std::stringstream ss; ss << tmp->asUInt(); return ss.str(); } if (tmp->isDouble()) { std::stringstream ss; ss << tmp->asDouble(); 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; }