string BSONElement::toString( bool includeFieldName ) const { stringstream s; if ( includeFieldName && type() != EOO ) s << fieldName() << ": "; switch ( type() ) { case EOO: return "EOO"; case Date: s << "Date(" << hex << date() << ')'; break; case RegEx: { s << "/" << regex() << '/'; const char *p = regexFlags(); if ( p ) s << p; } break; case NumberDouble: { stringstream tmp; tmp.precision( 16 ); tmp << number(); string n = tmp.str(); s << n; // indicate this is a double: if( strchr(n.c_str(), '.') == 0 && strchr(n.c_str(), 'E') == 0 && strchr(n.c_str(), 'N') == 0 ) s << ".0"; } break; case NumberInt: s.precision( 16 ); s << number(); //s << "(" << ( type() == NumberInt ? "int" : "double" ) << ")"; break; case Bool: s << ( boolean() ? "true" : "false" ); break; case Object: case Array: s << embeddedObject().toString(); break; case Undefined: s << "undefined"; break; case jstNULL: s << "null"; break; case MaxKey: s << "MaxKey"; break; case MinKey: s << "MinKey"; break; case CodeWScope: s << "CodeWScope( " << codeWScopeCode() << ", " << codeWScopeObject().toString() << ")"; break; case Code: if ( valuestrsize() > 80 ) s << string(valuestr()).substr(0, 70) << "..."; else { s << valuestr(); } break; case Symbol: case String: if ( valuestrsize() > 80 ) s << '"' << string(valuestr()).substr(0, 70) << "...\""; else { s << '"' << valuestr() << '"'; } break; case DBRef: s << "DBRef('" << valuestr() << "',"; { OID *x = (OID *) (valuestr() + valuestrsize()); s << *x << ')'; } break; case jstOID: s << "ObjId("; s << __oid() << ')'; break; case BinData: s << "BinData"; break; case Timestamp: s << "Timestamp " << timestampTime() << "|" << timestampInc(); break; default: s << "?type=" << type(); break; } return s.str(); }
void BSONElement::toString( StringBuilder& s, bool includeFieldName, bool full, bool redactValues, int depth) const { if (depth > BSONObj::maxToStringRecursionDepth) { // check if we want the full/complete string if (full) { StringBuilder s; s << "Reached maximum recursion depth of "; s << BSONObj::maxToStringRecursionDepth; uassert(16150, s.str(), full != true); } s << "..."; return; } if (includeFieldName && type() != EOO) s << fieldName() << ": "; switch (type()) { case Object: return embeddedObject().toString(s, false, full, redactValues, depth + 1); case mongo::Array: return embeddedObject().toString(s, true, full, redactValues, depth + 1); default: break; } if (redactValues) { s << "\"###\""; return; } switch (type()) { case EOO: s << "EOO"; break; case mongo::Date: s << "new Date(" << date().toMillisSinceEpoch() << ')'; break; case RegEx: { s << "/" << regex() << '/'; const char* p = regexFlags(); if (p) s << p; } break; case NumberDouble: s.appendDoubleNice(number()); break; case NumberLong: s << _numberLong(); break; case NumberInt: s << _numberInt(); break; case NumberDecimal: s << _numberDecimal().toString(); break; case mongo::Bool: s << (boolean() ? "true" : "false"); break; case Undefined: s << "undefined"; break; case jstNULL: s << "null"; break; case MaxKey: s << "MaxKey"; break; case MinKey: s << "MinKey"; break; case CodeWScope: s << "CodeWScope( " << codeWScopeCode() << ", " << codeWScopeObject().toString() << ")"; break; case Code: if (!full && valuestrsize() > 80) { s.write(valuestr(), 70); s << "..."; } else { s.write(valuestr(), valuestrsize() - 1); } break; case Symbol: case mongo::String: s << '"'; if (!full && valuestrsize() > 160) { s.write(valuestr(), 150); s << "...\""; } else { s.write(valuestr(), valuestrsize() - 1); s << '"'; } break; case DBRef: s << "DBRef('" << valuestr() << "',"; s << mongo::OID::from(valuestr() + valuestrsize()) << ')'; break; case jstOID: s << "ObjectId('"; s << __oid() << "')"; break; case BinData: { int len; const char* data = binDataClean(len); // If the BinData is a correctly sized newUUID, display it as such. if (binDataType() == newUUID && len == 16) { // 4 Octets - 2 Octets - 2 Octets - 2 Octets - 6 Octets s << "UUID(\""; s << toHexLower(&data[0], 4); s << "-"; s << toHexLower(&data[4], 2); s << "-"; s << toHexLower(&data[6], 2); s << "-"; s << toHexLower(&data[8], 2); s << "-"; s << toHexLower(&data[10], 6); s << "\")"; break; } s << "BinData(" << binDataType() << ", "; if (!full && len > 80) { s << toHex(data, 70) << "...)"; } else { s << toHex(data, len) << ")"; } } break; case bsonTimestamp: { // Convert from Milliseconds to Seconds for consistent Timestamp printing. auto secs = duration_cast<Seconds>(timestampTime().toDurationSinceEpoch()); s << "Timestamp(" << secs.count() << ", " << timestampInc() << ")"; } break; default: s << "?type=" << type(); break; } }
string BSONElement::jsonString( JsonStringFormat format, bool includeFieldNames, int pretty ) const { BSONType t = type(); if ( t == Undefined ) return ""; stringstream s; if ( includeFieldNames ) s << '"' << escape( fieldName() ) << "\" : "; switch ( type() ) { case mongo::String: case Symbol: s << '"' << escape( string(valuestr(), valuestrsize()-1) ) << '"'; break; case NumberLong: s << _numberLong(); break; case NumberInt: case NumberDouble: if ( number() >= -numeric_limits< double >::max() && number() <= numeric_limits< double >::max() ) { s.precision( 16 ); s << number(); } else { stringstream ss; ss << "Number " << number() << " cannot be represented in JSON"; string message = ss.str(); massert( 10311 , message.c_str(), false ); } break; case mongo::Bool: s << ( boolean() ? "true" : "false" ); break; case jstNULL: s << "null"; break; case Object: s << embeddedObject().jsonString( format, pretty ); break; case mongo::Array: { if ( embeddedObject().isEmpty() ) { s << "[]"; break; } s << "[ "; BSONObjIterator i( embeddedObject() ); BSONElement e = i.next(); if ( !e.eoo() ) while ( 1 ) { if( pretty ) { s << '\n'; for( int x = 0; x < pretty; x++ ) s << " "; } s << e.jsonString( format, false, pretty?pretty+1:0 ); e = i.next(); if ( e.eoo() ) break; s << ", "; } s << " ]"; break; } case DBRef: { mongo::OID *x = (mongo::OID *) (valuestr() + valuestrsize()); if ( format == TenGen ) s << "Dbref( "; else s << "{ \"$ref\" : "; s << '"' << valuestr() << "\", "; if ( format != TenGen ) s << "\"$id\" : "; s << '"' << *x << "\" "; if ( format == TenGen ) s << ')'; else s << '}'; break; } case jstOID: if ( format == TenGen ) { s << "ObjectId( "; } else { s << "{ \"$oid\" : "; } s << '"' << __oid() << '"'; if ( format == TenGen ) { s << " )"; } else { s << " }"; } break; case BinData: { int len = *(int *)( value() ); BinDataType type = BinDataType( *(char *)( (int *)( value() ) + 1 ) ); s << "{ \"$binary\" : \""; char *start = ( char * )( value() ) + sizeof( int ) + 1; base64::encode( s , start , len ); s << "\", \"$type\" : \"" << hex; s.width( 2 ); s.fill( '0' ); s << type << dec; s << "\" }"; break; } case mongo::Date: if ( format == Strict ) s << "{ \"$date\" : "; else s << "Date( "; if( pretty ) { Date_t d = date(); if( d == 0 ) s << '0'; else s << '"' << date().toString() << '"'; } else s << date(); if ( format == Strict ) s << " }"; else s << " )"; break; case RegEx: if ( format == Strict ){ s << "{ \"$regex\" : \"" << escape( regex() ); s << "\", \"$options\" : \"" << regexFlags() << "\" }"; } else { s << "/" << escape( regex() , true ) << "/"; // FIXME Worry about alpha order? for ( const char *f = regexFlags(); *f; ++f ){ switch ( *f ) { case 'g': case 'i': case 'm': s << *f; default: break; } } } break; case CodeWScope: { BSONObj scope = codeWScopeObject(); if ( ! scope.isEmpty() ){ s << "{ \"$code\" : " << _asCode() << " , " << " \"$scope\" : " << scope.jsonString() << " }"; break; } } case Code: s << _asCode(); break; case Timestamp: s << "{ \"t\" : " << timestampTime() << " , \"i\" : " << timestampInc() << " }"; break; case MinKey: s << "{ \"$minKey\" : 1 }"; break; case MaxKey: s << "{ \"$maxKey\" : 1 }"; break; default: stringstream ss; ss << "Cannot create a properly formatted JSON string with " << "element: " << toString() << " of type: " << type(); string message = ss.str(); massert( 10312 , message.c_str(), false ); } return s.str(); }
void BSONElement::jsonStringStream(JsonStringFormat format, bool includeFieldNames, int pretty, std::stringstream& s) const { if (includeFieldNames) s << '"' << escape(fieldName()) << "\" : "; switch (type()) { case mongo::String: case Symbol: s << '"' << escape(string(valuestr(), valuestrsize() - 1)) << '"'; break; case NumberLong: if (format == TenGen) { s << "NumberLong(" << _numberLong() << ")"; } else { s << "{ \"$numberLong\" : \"" << _numberLong() << "\" }"; } break; case NumberInt: if (format == TenGen) { s << "NumberInt(" << _numberInt() << ")"; break; } case NumberDouble: if (number() >= -std::numeric_limits<double>::max() && number() <= std::numeric_limits<double>::max()) { auto origPrecision = s.precision(); auto guard = MakeGuard([&s, origPrecision]() { s.precision(origPrecision); }); s.precision(16); s << number(); } // This is not valid JSON, but according to RFC-4627, "Numeric values that cannot be // represented as sequences of digits (such as Infinity and NaN) are not permitted." so // we are accepting the fact that if we have such values we cannot output valid JSON. else if (std::isnan(number())) { s << "NaN"; } else if (std::isinf(number())) { s << (number() > 0 ? "Infinity" : "-Infinity"); } else { StringBuilder ss; ss << "Number " << number() << " cannot be represented in JSON"; string message = ss.str(); massert(10311, message.c_str(), false); } break; case NumberDecimal: if (format == TenGen) s << "NumberDecimal(\""; else s << "{ \"$numberDecimal\" : \""; // Recognize again that this is not valid JSON according to RFC-4627. // Also, treat -NaN and +NaN as the same thing for MongoDB. if (numberDecimal().isNaN()) { s << "NaN"; } else if (numberDecimal().isInfinite()) { s << (numberDecimal().isNegative() ? "-Infinity" : "Infinity"); } else { s << numberDecimal().toString(); } if (format == TenGen) s << "\")"; else s << "\" }"; break; case mongo::Bool: s << (boolean() ? "true" : "false"); break; case jstNULL: s << "null"; break; case Undefined: if (format == Strict) { s << "{ \"$undefined\" : true }"; } else { s << "undefined"; } break; case Object: embeddedObject().jsonStringStream(format, pretty, false, s); break; case mongo::Array: { if (embeddedObject().isEmpty()) { s << "[]"; break; } s << "[ "; BSONObjIterator i(embeddedObject()); BSONElement e = i.next(); if (!e.eoo()) { int count = 0; while (1) { if (pretty) { s << '\n'; for (int x = 0; x < pretty; x++) s << " "; } if (strtol(e.fieldName(), 0, 10) > count) { s << "undefined"; } else { e.jsonStringStream(format, false, pretty ? pretty + 1 : 0, s); e = i.next(); } count++; if (e.eoo()) break; s << ", "; } } s << " ]"; break; } case DBRef: { if (format == TenGen) s << "Dbref( "; else s << "{ \"$ref\" : "; s << '"' << valuestr() << "\", "; if (format != TenGen) s << "\"$id\" : "; s << '"' << mongo::OID::from(valuestr() + valuestrsize()) << "\" "; if (format == TenGen) s << ')'; else s << '}'; break; } case jstOID: if (format == TenGen) { s << "ObjectId( "; } else { s << "{ \"$oid\" : "; } s << '"' << __oid() << '"'; if (format == TenGen) { s << " )"; } else { s << " }"; } break; case BinData: { ConstDataCursor reader(value()); const int len = reader.readAndAdvance<LittleEndian<int>>(); BinDataType type = static_cast<BinDataType>(reader.readAndAdvance<uint8_t>()); s << "{ \"$binary\" : \""; base64::encode(s, reader.view(), len); auto origFill = s.fill(); auto origFmtF = s.flags(); auto origWidth = s.width(); auto guard = MakeGuard([&s, origFill, origFmtF, origWidth] { s.fill(origFill); s.setf(origFmtF); s.width(origWidth); }); s.setf(std::ios_base::hex, std::ios_base::basefield); s << "\", \"$type\" : \""; s.width(2); s.fill('0'); s << type; s << "\" }"; break; } case mongo::Date: if (format == Strict) { Date_t d = date(); s << "{ \"$date\" : "; // The two cases in which we cannot convert Date_t::millis to an ISO Date string are // when the date is too large to format (SERVER-13760), and when the date is before // the epoch (SERVER-11273). Since Date_t internally stores millis as an unsigned // long long, despite the fact that it is logically signed (SERVER-8573), this check // handles both the case where Date_t::millis is too large, and the case where // Date_t::millis is negative (before the epoch). if (d.isFormattable()) { s << "\"" << dateToISOStringLocal(date()) << "\""; } else { s << "{ \"$numberLong\" : \"" << d.toMillisSinceEpoch() << "\" }"; } s << " }"; } else { s << "Date( "; if (pretty) { Date_t d = date(); // The two cases in which we cannot convert Date_t::millis to an ISO Date string // are when the date is too large to format (SERVER-13760), and when the date is // before the epoch (SERVER-11273). Since Date_t internally stores millis as an // unsigned long long, despite the fact that it is logically signed // (SERVER-8573), this check handles both the case where Date_t::millis is too // large, and the case where Date_t::millis is negative (before the epoch). if (d.isFormattable()) { s << "\"" << dateToISOStringLocal(date()) << "\""; } else { // FIXME: This is not parseable by the shell, since it may not fit in a // float s << d.toMillisSinceEpoch(); } } else { s << date().asInt64(); } s << " )"; } break; case RegEx: if (format == Strict) { s << "{ \"$regex\" : \"" << escape(regex()); s << "\", \"$options\" : \"" << regexFlags() << "\" }"; } else { s << "/" << escape(regex(), true) << "/"; // FIXME Worry about alpha order? for (const char* f = regexFlags(); *f; ++f) { switch (*f) { case 'g': case 'i': case 'm': s << *f; default: break; } } } break; case CodeWScope: { BSONObj scope = codeWScopeObject(); if (!scope.isEmpty()) { s << "{ \"$code\" : \"" << escape(_asCode()) << "\" , " << "\"$scope\" : " << scope.jsonString() << " }"; break; } } case Code: s << "\"" << escape(_asCode()) << "\""; break; case bsonTimestamp: if (format == TenGen) { s << "Timestamp( " << durationCount<Seconds>(timestampTime().toDurationSinceEpoch()) << ", " << timestampInc() << " )"; } else { s << "{ \"$timestamp\" : { \"t\" : " << durationCount<Seconds>(timestampTime().toDurationSinceEpoch()) << ", \"i\" : " << timestampInc() << " } }"; } break; case MinKey: s << "{ \"$minKey\" : 1 }"; break; case MaxKey: s << "{ \"$maxKey\" : 1 }"; break; default: StringBuilder ss; ss << "Cannot create a properly formatted JSON string with " << "element: " << toString() << " of type: " << type(); string message = ss.str(); massert(10312, message.c_str(), false); } }