diff options
author | Tyler Brock <tyler.brock@gmail.com> | 2014-06-19 14:38:00 -0400 |
---|---|---|
committer | Tyler Brock <tyler.brock@gmail.com> | 2014-06-25 15:01:59 -0400 |
commit | cdd1172ad392b8c2321e4e140b020edf8c97b7c9 (patch) | |
tree | f4588ef7f130a7ca28161154b1aeaae7d0539628 /src | |
parent | 11ea31ab1ac1109af219f7a36fb21dcf7dbbe5da (diff) | |
download | mongo-cdd1172ad392b8c2321e4e140b020edf8c97b7c9.tar.gz |
SERVER-14357: Add support for top level JSON Array
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/bson/bsonobj.h | 6 | ||||
-rw-r--r-- | src/mongo/db/jsobj.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/json.cpp | 45 | ||||
-rw-r--r-- | src/mongo/db/json.h | 48 | ||||
-rw-r--r-- | src/mongo/dbtests/jsontests.cpp | 31 |
5 files changed, 121 insertions, 19 deletions
diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h index 18082e562ad..08055ecbb22 100644 --- a/src/mongo/bson/bsonobj.h +++ b/src/mongo/bson/bsonobj.h @@ -161,7 +161,11 @@ namespace mongo { /** Properly formatted JSON string. @param pretty if true we try to add some lf's and indentation */ - std::string jsonString( JsonStringFormat format = Strict, int pretty = 0 ) const; + std::string jsonString( + JsonStringFormat format = Strict, + int pretty = 0, + bool isArray = false + ) const; /** note: addFields always adds _id even if not specified */ int addFields(BSONObj& from, std::set<std::string>& fields); /* returns n added */ diff --git a/src/mongo/db/jsobj.cpp b/src/mongo/db/jsobj.cpp index ab0a1805c3f..ed3f1cf5de7 100644 --- a/src/mongo/db/jsobj.cpp +++ b/src/mongo/db/jsobj.cpp @@ -490,17 +490,17 @@ namespace mongo { return digestToString( d ); } - string BSONObj::jsonString( JsonStringFormat format, int pretty ) const { + string BSONObj::jsonString( JsonStringFormat format, int pretty, bool isArray ) const { - if ( isEmpty() ) return "{}"; + if ( isEmpty() ) return isArray ? "[]" : "{}"; StringBuilder s; - s << "{ "; + s << (isArray ? "[ " : "{ "); BSONObjIterator i(*this); BSONElement e = i.next(); if ( !e.eoo() ) while ( 1 ) { - s << e.jsonString( format, true, pretty?pretty+1:0 ); + s << e.jsonString( format, !isArray, pretty?pretty+1:0 ); e = i.next(); if ( e.eoo() ) break; @@ -514,7 +514,7 @@ namespace mongo { s << " "; } } - s << " }"; + s << (isArray ? " ]" : " }"); return s.str(); } diff --git a/src/mongo/db/json.cpp b/src/mongo/db/json.cpp index ddf5bb254fd..0162fbf860c 100644 --- a/src/mongo/db/json.cpp +++ b/src/mongo/db/json.cpp @@ -77,8 +77,11 @@ namespace mongo { *SINGLEQUOTE = "'", *DOUBLEQUOTE = "\""; - JParse::JParse(const char* str) - : _buf(str), _input(str), _input_end(str + strlen(str)) {} + JParse::JParse(const StringData& str) + : _buf(str.rawData()) + , _input(_buf) + , _input_end(_input + str.size()) + {} Status JParse::parseError(const StringData& msg) { std::ostringstream ossmsg; @@ -191,6 +194,10 @@ namespace mongo { return Status::OK(); } + Status JParse::parse(BSONObjBuilder& builder) { + return isArray() ? array("UNUSED", builder, false) : object("UNUSED", builder, false); + } + Status JParse::object(const StringData& fieldName, BSONObjBuilder& builder, bool subObject) { MONGO_JSON_DEBUG("fieldName: " << fieldName); if (!readToken(LBRACE)) { @@ -645,23 +652,30 @@ namespace mongo { return Status::OK(); } - Status JParse::array(const StringData& fieldName, BSONObjBuilder& builder) { + Status JParse::array(const StringData& fieldName, BSONObjBuilder& builder, bool subObject) { MONGO_JSON_DEBUG("fieldName: " << fieldName); uint32_t index(0); if (!readToken(LBRACKET)) { return parseError("Expecting '['"); } - BSONObjBuilder subBuilder(builder.subarrayStart(fieldName)); + + BSONObjBuilder* arrayBuilder = &builder; + scoped_ptr<BSONObjBuilder> subObjBuilder; + if (subObject) { + subObjBuilder.reset(new BSONObjBuilder(builder.subarrayStart(fieldName))); + arrayBuilder = subObjBuilder.get(); + } + if (!peekToken(RBRACKET)) { do { - Status ret = value(builder.numStr(index), subBuilder); + Status ret = value(builder.numStr(index), *arrayBuilder); if (ret != Status::OK()) { return ret; } index++; } while (readToken(COMMA)); } - subBuilder.done(); + arrayBuilder->done(); if (!readToken(RBRACKET)) { return parseError("Expecting ']' or ','"); } @@ -1190,6 +1204,10 @@ namespace mongo { return true; } + bool JParse::isArray() { + return peekToken(LBRACKET); + } + BSONObj fromjson(const char* jsonString, int* len) { MONGO_JSON_DEBUG("jsonString: " << jsonString); if (jsonString[0] == '\0') { @@ -1200,7 +1218,7 @@ namespace mongo { BSONObjBuilder builder; Status ret = Status::OK(); try { - ret = jparse.object("UNUSED", builder, false); + ret = jparse.parse(builder); } catch(std::exception& e) { std::ostringstream message; @@ -1221,4 +1239,17 @@ namespace mongo { return fromjson( str.c_str() ); } + std::string tojson(const BSONObj& obj, JsonStringFormat format, bool pretty) { + return obj.jsonString(format, pretty); + } + + std::string tojson(const BSONArray& arr, JsonStringFormat format, bool pretty) { + return arr.jsonString(format, pretty, true); + } + + bool isArray(const StringData& str) { + JParse parser(str); + return parser.isArray(); + } + } /* namespace mongo */ diff --git a/src/mongo/db/json.h b/src/mongo/db/json.h index 4df19f756d5..0d3d92c126d 100644 --- a/src/mongo/db/json.h +++ b/src/mongo/db/json.h @@ -55,13 +55,55 @@ namespace mongo { MONGO_CLIENT_API BSONObj fromjson(const char* str, int* len=NULL); /** + * Tests whether the JSON string is an Array. + * + * Useful for assigning the result of fromjson to the right object type. Either: + * BSONObj + * BSONArray + * + * @example Using the method to select the proper type. + * If this method returns true, the user could store the result of fromjson + * inside a BSONArray, rather than a BSONObj, in order to have it print as an + * array when passed to tojson. + * + * @param obj The JSON string to test. + */ + MONGO_CLIENT_API bool isArray(const StringData& str); + + /** + * Convert a BSONArray to a JSON string. + * + * @param arr The BSON Array. + * @param format The JSON format (JS, TenGen, Strict). + * @param pretty Enables pretty output. + */ + MONGO_CLIENT_API std::string tojson( + const BSONArray& arr, + JsonStringFormat format = Strict, + bool pretty = false + ); + + /** + * Convert a BSONObj to a JSON string. + * + * @param obj The BSON Object. + * @param format The JSON format (JS, TenGen, Strict). + * @param pretty Enables pretty output. + */ + MONGO_CLIENT_API std::string tojson( + const BSONObj& obj, + JsonStringFormat format = Strict, + bool pretty = false + ); + + /** * Parser class. A BSONObj is constructed incrementally by passing a * BSONObjBuilder to the recursive parsing methods. The grammar for the * element parsed is described before each function. */ class JParse { public: - explicit JParse(const char*); + explicit JParse(const StringData& str); /* * Notation: All-uppercase symbols denote non-terminals; all other @@ -123,6 +165,8 @@ namespace mongo { */ public: Status object(const StringData& fieldName, BSONObjBuilder&, bool subObj=true); + Status parse(BSONObjBuilder& builder); + bool isArray(); private: /* The following functions are called with the '{' and the first @@ -196,7 +240,7 @@ namespace mongo { * VALUE * | VALUE , ELEMENTS */ - Status array(const StringData& fieldName, BSONObjBuilder&); + Status array(const StringData& fieldName, BSONObjBuilder&, bool subObj=true); /* * NOTE: Currently only Date can be preceded by the "new" keyword diff --git a/src/mongo/dbtests/jsontests.cpp b/src/mongo/dbtests/jsontests.cpp index c7aa8eedb8a..607e319a081 100644 --- a/src/mongo/dbtests/jsontests.cpp +++ b/src/mongo/dbtests/jsontests.cpp @@ -578,10 +578,10 @@ namespace JsonTests { virtual ~Base() {} void run() { ASSERT( fromjson( json() ).valid() ); - assertEquals( bson(), fromjson( json() ), "mode: <default>" ); - assertEquals( bson(), fromjson( bson().jsonString( Strict ) ), "mode: strict" ); - assertEquals( bson(), fromjson( bson().jsonString( TenGen ) ), "mode: tengen" ); - assertEquals( bson(), fromjson( bson().jsonString( JS ) ), "mode: js" ); + assertEquals( bson(), fromjson( tojson( bson() ) ), "mode: <default>" ); + assertEquals( bson(), fromjson( tojson( bson(), Strict ) ), "mode: strict" ); + assertEquals( bson(), fromjson( tojson( bson(), TenGen ) ), "mode: tengen" ); + assertEquals( bson(), fromjson( tojson( bson(), JS ) ), "mode: js" ); } protected: virtual BSONObj bson() const = 0; @@ -814,6 +814,27 @@ namespace JsonTests { } }; + class TopLevelArrayEmpty : public Base { + virtual BSONObj bson() const { + return BSONArray(); + } + virtual string json() const { + return "[]"; + } + }; + + class TopLevelArray : public Base { + virtual BSONObj bson() const { + BSONArrayBuilder builder; + builder.append(123); + builder.append("abc"); + return builder.arr(); + } + virtual string json() const { + return "[ 123, \"abc\" ]"; + } + }; + class Array : public Base { virtual BSONObj bson() const { vector< int > arr; @@ -2675,6 +2696,8 @@ namespace JsonTests { add< FromJsonTests::Subobject >(); add< FromJsonTests::DeeplyNestedObject >(); add< FromJsonTests::ArrayEmpty >(); + add< FromJsonTests::TopLevelArrayEmpty >(); + add< FromJsonTests::TopLevelArray >(); add< FromJsonTests::Array >(); add< FromJsonTests::True >(); add< FromJsonTests::False >(); |