#ifndef __JString_h__ #define __JString_h__ #include "MString.h" #include #include namespace coreutils { /// /// Use the JString object when you need a JSON interface to C++. JString uses /// an MString to store a JSON string representing the object(s) contained. /// The [] operator is overriden from the ZString and provides a method to /// access the objects elements using a named path as the index to the items. /// /// JString default constructor will create an empty JString object pointing /// to an empty object ("{}"). /// /// Use a constructor or assignment operator to initialize a JString to an /// initial JSON string. /// /// Several constructors are available including a handle to a std::FILE /// object to initialize the JSON string. On destruction the file is saved back. /// /// The [] operator can be used to access elements within the JSON string. /// Assigning a value dispatches on the C++ type of the right-hand side, so the /// resulting JSON is correctly typed rather than always being a quoted string: /// /// jstring["name"] = "value"; // JSON string, escaped correctly /// jstring["count"] = 42; // JSON number (integer) /// jstring["price"] = 19.95; // JSON number /// jstring["active"] = true; // JSON boolean /// jstring["notes"].setNull(); // JSON null /// /// std::cout << jstring["name"]; /// /// Typed read access is available directly on the path (no need to go through /// operator[] first): /// /// std::string name = jstring.getString("name"); /// double price = jstring.getNumber("price"); /// bool active = jstring.getBool("active"); /// bool missing = !jstring.exists("notfound"); /// JSONType t = jstring.getType("count"); // JSONType::NUMBER /// enum class JSONType { STRING, NUMBER, BOOLEAN, JNULL, ARRAY, OBJECT, NOT_FOUND }; class JString : virtual public MString { public: JString(); void set(ZString &path, ZString &value); /// /// Use the find method to look through an object for member specified by /// the key in parameter 1. The cursor must be pointing to the { character /// that begins the object's list. Returns the raw (still-escaped, for /// strings) span of the value - use getString()/getNumber()/getBool()/ /// isNull() for decoded, typed access instead. /// ZString find(ZString path); bool locate(ZString path); bool notfirst; ZString path; /// /// Type-aware access. All of these call locate() internally, so path uses /// the same dotted/bracketed syntax as find()/set() (e.g. "a.b[2].c"). /// JSONType getType(ZString path); bool exists(ZString path); bool isNull(ZString path); std::string getString(ZString path); ///< Escape-decoded string content. double getNumber(ZString path); long getInt(ZString path); bool getBool(ZString path); void setString(ZString path, std::string value); ///< Escapes value automatically. void setNumber(ZString path, double value); void setInt(ZString path, long value); void setBool(ZString path, bool value); void setNull(ZString path); /// /// JSON string escape/unescape helpers (exposed as static utilities since /// they operate on plain text, not on this object's own JSON document). /// static std::string jsonEscape(ZString value); static std::string jsonUnescape(ZString raw); class Proxy { public: Proxy(JString &jstring, ZString key) : jstring(jstring), key(key) {} friend std::ostream &operator<<(std::ostream &os, const Proxy &proxy); /// /// Proxy offers several implicit conversion operators below (to /// ZString, MString, std::string, double, bool) so a single $[...]- /// style access can be used wherever any of those types is expected. /// That flexibility makes "MString << Proxy" genuinely ambiguous on /// its own - operator ZString() and operator MString() (via its own /// implicit upcast to ZString) both satisfy MString::operator<<(ZString), /// and operator double()/operator bool() both satisfy /// MString::operator<<(int) via a standard numeric conversion - so /// the compiler has no way to prefer one path over another. This /// exact-match overload (mirroring the ostream one above, which /// solves the same problem for std::cout << proxy) resolves it by /// giving MString a candidate that needs no conversion at all. /// friend MString &operator<<(MString &ms, const Proxy &proxy); operator coreutils::ZString () { return jstring.find(key); } operator coreutils::MString () { return jstring.find(key); } operator std::string () { return jstring.getString(key); } operator double () { return jstring.getNumber(key); } operator bool () { return jstring.getBool(key); } Proxy& operator=(coreutils::MString value) { jstring.setString(key, value.str()); jstring.changed(key, value); return *this; } Proxy& operator=(const char *value) { jstring.setString(key, std::string(value)); jstring.changed(key, ZString(value)); return *this; } Proxy& operator=(double value) { jstring.setNumber(key, value); return *this; } Proxy& operator=(int value) { jstring.setInt(key, value); return *this; } Proxy& operator=(long value) { jstring.setInt(key, value); return *this; } Proxy& operator=(bool value) { jstring.setBool(key, value); return *this; } void setNull() { jstring.setNull(key); } private: JString &jstring; ZString key; }; Proxy operator[](coreutils::ZString key) { return Proxy(*this, key); } JString & operator=(coreutils:: ZString value); MString pretty(); virtual void changed(ZString key, ZString value); protected: /// /// Strips whitespace between JSON structural tokens (indentation, /// newlines, spaces after ':'/',') - the parser expects compact JSON /// internally. operator=(ZString) already calls this for in-memory /// assignment; JFile's constructor also calls it after loading a /// file's raw bytes, which otherwise bypasses this entirely (see /// JFile.cpp). Protected rather than private specifically so JFile /// can reach it. /// void removeWhitespace(); private: /// /// Structural helpers used internally by locate()/set() to correctly skip /// past a value of any JSON type without being confused by JSON-structural /// characters (quotes, braces, brackets) appearing inside string literals. /// ZString findRelative(ZString path); JSONType typeAtCursor(); void skipValue(); void skipString(); void skipContainer(); void skipNumber(); ZString captureString(); ZString captureContainer(); bool isLiteralValue(ZString value); }; } #endif