| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643 |
- /* Copyright 2010-2014 MongoDB Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Text.RegularExpressions;
- using System.Xml;
- namespace MongoDB.Bson.IO
- {
- /// <summary>
- /// Represents a BSON reader for a JSON string.
- /// </summary>
- public class JsonReader : BsonReader
- {
- // private fields
- private JsonBuffer _buffer;
- private JsonReaderSettings _jsonReaderSettings; // same value as in base class just declared as derived class
- private JsonReaderContext _context;
- private JsonToken _currentToken;
- private BsonValue _currentValue;
- private JsonToken _pushedToken;
- // constructors
- /// <summary>
- /// Initializes a new instance of the JsonReader class.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="settings">The reader settings.</param>
- public JsonReader(JsonBuffer buffer, JsonReaderSettings settings)
- : base(settings)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException("buffer");
- }
- _buffer = buffer;
- _jsonReaderSettings = settings; // already frozen by base class
- _context = new JsonReaderContext(null, ContextType.TopLevel);
- }
- // public methods
- /// <summary>
- /// Closes the reader.
- /// </summary>
- public override void Close()
- {
- // Close can be called on Disposed objects
- if (State != BsonReaderState.Closed)
- {
- State = BsonReaderState.Closed;
- }
- }
- /// <summary>
- /// Gets a bookmark to the reader's current position and state.
- /// </summary>
- /// <returns>A bookmark.</returns>
- public override BsonReaderBookmark GetBookmark()
- {
- return new JsonReaderBookmark(State, CurrentBsonType, CurrentName, _context, _currentToken, _currentValue, _pushedToken, _buffer.Position);
- }
- /// <summary>
- /// Reads BSON binary data from the reader.
- /// </summary>
- /// <returns>A BsonBinaryData.</returns>
- public override BsonBinaryData ReadBinaryData()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadBinaryData", BsonType.Binary);
- State = GetNextState();
- return _currentValue.AsBsonBinaryData;
- }
- /// <summary>
- /// Reads a BSON boolean from the reader.
- /// </summary>
- /// <returns>A Boolean.</returns>
- public override bool ReadBoolean()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadBoolean", BsonType.Boolean);
- State = GetNextState();
- return _currentValue.AsBoolean;
- }
- /// <summary>
- /// Reads a BsonType from the reader.
- /// </summary>
- /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
- /// <param name="bsonTrie">An optional trie to search for a value that matches the next element name.</param>
- /// <param name="found">Set to true if a matching value was found in the trie.</param>
- /// <param name="value">Set to the matching value found in the trie or null if no matching value was found.</param>
- /// <returns>A BsonType.</returns>
- public override BsonType ReadBsonType<TValue>(BsonTrie<TValue> bsonTrie, out bool found, out TValue value)
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- found = false;
- value = default(TValue);
- if (State == BsonReaderState.Initial || State == BsonReaderState.Done || State == BsonReaderState.ScopeDocument)
- {
- // in JSON the top level value can be of any type so fall through
- State = BsonReaderState.Type;
- }
- if (State != BsonReaderState.Type)
- {
- ThrowInvalidState("ReadBsonType", BsonReaderState.Type);
- }
- if (_context.ContextType == ContextType.Document)
- {
- var nameToken = PopToken();
- switch (nameToken.Type)
- {
- case JsonTokenType.String:
- case JsonTokenType.UnquotedString:
- if (bsonTrie != null)
- {
- found = bsonTrie.TryGetValue(nameToken.StringValue, out value);
- }
- CurrentName = nameToken.StringValue;
- break;
- case JsonTokenType.EndObject:
- State = BsonReaderState.EndOfDocument;
- return BsonType.EndOfDocument;
- default:
- var message = string.Format("JSON reader was expecting a name but found '{0}'.", nameToken.Lexeme);
- throw new Exception(message);
- }
- var colonToken = PopToken();
- if (colonToken.Type != JsonTokenType.Colon)
- {
- var message = string.Format("JSON reader was expecting ':' but found '{0}'.", colonToken.Lexeme);
- throw new Exception(message);
- }
- }
- var valueToken = PopToken();
- if (_context.ContextType == ContextType.Array && valueToken.Type == JsonTokenType.EndArray)
- {
- State = BsonReaderState.EndOfArray;
- return BsonType.EndOfDocument;
- }
- var noValueFound = false;
- switch (valueToken.Type)
- {
- case JsonTokenType.BeginArray:
- CurrentBsonType = BsonType.Array;
- break;
- case JsonTokenType.BeginObject:
- CurrentBsonType = ParseExtendedJson();
- break;
- case JsonTokenType.DateTime:
- CurrentBsonType = BsonType.DateTime;
- _currentValue = valueToken.DateTimeValue;
- break;
- case JsonTokenType.Double:
- CurrentBsonType = BsonType.Double;
- _currentValue = valueToken.DoubleValue;
- break;
- case JsonTokenType.EndOfFile:
- CurrentBsonType = BsonType.EndOfDocument;
- break;
- case JsonTokenType.Int32:
- CurrentBsonType = BsonType.Int32;
- _currentValue = valueToken.Int32Value;
- break;
- case JsonTokenType.Int64:
- CurrentBsonType = BsonType.Int64;
- _currentValue = valueToken.Int64Value;
- break;
- case JsonTokenType.ObjectId:
- CurrentBsonType = BsonType.ObjectId;
- _currentValue = valueToken.ObjectIdValue;
- break;
- case JsonTokenType.RegularExpression:
- CurrentBsonType = BsonType.RegularExpression;
- _currentValue = valueToken.RegularExpressionValue;
- break;
- case JsonTokenType.String:
- CurrentBsonType = BsonType.String;
- _currentValue = valueToken.StringValue;
- break;
- case JsonTokenType.UnquotedString:
- switch (valueToken.Lexeme)
- {
- case "false":
- case "true":
- CurrentBsonType = BsonType.Boolean;
- _currentValue = XmlConvert.ToBoolean(valueToken.Lexeme);
- break;
- case "Infinity":
- CurrentBsonType = BsonType.Double;
- _currentValue = double.PositiveInfinity;
- break;
- case "NaN":
- CurrentBsonType = BsonType.Double;
- _currentValue = double.NaN;
- break;
- case "null":
- CurrentBsonType = BsonType.Null;
- break;
- case "undefined":
- CurrentBsonType = BsonType.Undefined;
- break;
- case "BinData":
- CurrentBsonType = BsonType.Binary;
- _currentValue = ParseBinDataConstructor();
- break;
- case "Date":
- CurrentBsonType = BsonType.String;
- _currentValue = ParseDateTimeConstructor(false); // withNew = false
- break;
- case "HexData":
- CurrentBsonType = BsonType.Binary;
- _currentValue = ParseHexDataConstructor();
- break;
- case "ISODate":
- CurrentBsonType = BsonType.DateTime;
- _currentValue = ParseISODateTimeConstructor();
- break;
- case "MaxKey":
- CurrentBsonType = BsonType.MaxKey;
- _currentValue = BsonMaxKey.Value;
- break;
- case "MinKey":
- CurrentBsonType = BsonType.MinKey;
- _currentValue = BsonMinKey.Value;
- break;
- case "Number":
- case "NumberInt":
- CurrentBsonType = BsonType.Int32;
- _currentValue = ParseNumberConstructor();
- break;
- case "NumberLong":
- CurrentBsonType = BsonType.Int64;
- _currentValue = ParseNumberLongConstructor();
- break;
- case "ObjectId":
- CurrentBsonType = BsonType.ObjectId;
- _currentValue = ParseObjectIdConstructor();
- break;
- case "RegExp":
- CurrentBsonType = BsonType.RegularExpression;
- _currentValue = ParseRegularExpressionConstructor();
- break;
- case "Timestamp":
- CurrentBsonType = BsonType.Timestamp;
- _currentValue = ParseTimestampConstructor();
- break;
- case "UUID":
- case "GUID":
- case "CSUUID":
- case "CSGUID":
- case "JUUID":
- case "JGUID":
- case "PYUUID":
- case "PYGUID":
- CurrentBsonType = BsonType.Binary;
- _currentValue = ParseUUIDConstructor(valueToken.Lexeme);
- break;
- case "new":
- CurrentBsonType = ParseNew(out _currentValue);
- break;
- default:
- noValueFound = true;
- break;
- }
- break;
- default:
- noValueFound = true;
- break;
- }
- if (noValueFound)
- {
- var message = string.Format("JSON reader was expecting a value but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- _currentToken = valueToken;
- if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.Document)
- {
- var commaToken = PopToken();
- if (commaToken.Type != JsonTokenType.Comma)
- {
- PushToken(commaToken);
- }
- }
- switch (_context.ContextType)
- {
- case ContextType.Document:
- case ContextType.ScopeDocument:
- default:
- State = BsonReaderState.Name;
- break;
- case ContextType.Array:
- case ContextType.JavaScriptWithScope:
- case ContextType.TopLevel:
- State = BsonReaderState.Value;
- break;
- }
- return CurrentBsonType;
- }
- /// <summary>
- /// Reads BSON binary data from the reader.
- /// </summary>
- /// <returns>A byte array.</returns>
- public override byte[] ReadBytes()
- {
- #pragma warning disable 618
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadBinaryData", BsonType.Binary);
- State = GetNextState();
- var binaryData = _currentValue.AsBsonBinaryData;
- var subType = binaryData.SubType;
- if (subType != BsonBinarySubType.Binary && subType != BsonBinarySubType.OldBinary)
- {
- var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {2}.", subType);
- throw new Exception(message);
- }
- return binaryData.Bytes;
- #pragma warning restore
- }
- /// <summary>
- /// Reads a BSON DateTime from the reader.
- /// </summary>
- /// <returns>The number of milliseconds since the Unix epoch.</returns>
- public override long ReadDateTime()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadDateTime", BsonType.DateTime);
- State = GetNextState();
- return _currentValue.AsBsonDateTime.MillisecondsSinceEpoch;
- }
- /// <summary>
- /// Reads a BSON Double from the reader.
- /// </summary>
- /// <returns>A Double.</returns>
- public override double ReadDouble()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadDouble", BsonType.Double);
- State = GetNextState();
- return _currentValue.AsDouble;
- }
- /// <summary>
- /// Reads the end of a BSON array from the reader.
- /// </summary>
- public override void ReadEndArray()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- if (_context.ContextType != ContextType.Array)
- {
- ThrowInvalidContextType("ReadEndArray", _context.ContextType, ContextType.Array);
- }
- if (State == BsonReaderState.Type)
- {
- ReadBsonType(); // will set state to EndOfArray if at end of array
- }
- if (State != BsonReaderState.EndOfArray)
- {
- ThrowInvalidState("ReadEndArray", BsonReaderState.EndOfArray);
- }
- _context = _context.PopContext();
- switch (_context.ContextType)
- {
- case ContextType.Array: State = BsonReaderState.Type; break;
- case ContextType.Document: State = BsonReaderState.Type; break;
- case ContextType.TopLevel: State = BsonReaderState.Done; break;
- default: throw new BsonInternalException("Unexpected ContextType.");
- }
- if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.Document)
- {
- var commaToken = PopToken();
- if (commaToken.Type != JsonTokenType.Comma)
- {
- PushToken(commaToken);
- }
- }
- }
- /// <summary>
- /// Reads the end of a BSON document from the reader.
- /// </summary>
- public override void ReadEndDocument()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- if (_context.ContextType != ContextType.Document && _context.ContextType != ContextType.ScopeDocument)
- {
- ThrowInvalidContextType("ReadEndDocument", _context.ContextType, ContextType.Document, ContextType.ScopeDocument);
- }
- if (State == BsonReaderState.Type)
- {
- ReadBsonType(); // will set state to EndOfDocument if at end of document
- }
- if (State != BsonReaderState.EndOfDocument)
- {
- ThrowInvalidState("ReadEndDocument", BsonReaderState.EndOfDocument);
- }
- _context = _context.PopContext();
- if (_context != null && _context.ContextType == ContextType.JavaScriptWithScope)
- {
- _context = _context.PopContext(); // JavaScriptWithScope
- VerifyToken("}"); // outermost closing bracket for JavaScriptWithScope
- }
- switch (_context.ContextType)
- {
- case ContextType.Array: State = BsonReaderState.Type; break;
- case ContextType.Document: State = BsonReaderState.Type; break;
- case ContextType.TopLevel: State = BsonReaderState.Done; break;
- default: throw new BsonInternalException("Unexpected ContextType");
- }
- if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.Document)
- {
- var commaToken = PopToken();
- if (commaToken.Type != JsonTokenType.Comma)
- {
- PushToken(commaToken);
- }
- }
- }
- /// <summary>
- /// Reads a BSON Int32 from the reader.
- /// </summary>
- /// <returns>An Int32.</returns>
- public override int ReadInt32()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadInt32", BsonType.Int32);
- State = GetNextState();
- return _currentValue.AsInt32;
- }
- /// <summary>
- /// Reads a BSON Int64 from the reader.
- /// </summary>
- /// <returns>An Int64.</returns>
- public override long ReadInt64()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadInt64", BsonType.Int64);
- State = GetNextState();
- return _currentValue.AsInt64;
- }
- /// <summary>
- /// Reads a BSON JavaScript from the reader.
- /// </summary>
- /// <returns>A string.</returns>
- public override string ReadJavaScript()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadJavaScript", BsonType.JavaScript);
- State = GetNextState();
- return _currentValue.AsString;
- }
- /// <summary>
- /// Reads a BSON JavaScript with scope from the reader (call ReadStartDocument next to read the scope).
- /// </summary>
- /// <returns>A string.</returns>
- public override string ReadJavaScriptWithScope()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadJavaScriptWithScope", BsonType.JavaScriptWithScope);
- _context = new JsonReaderContext(_context, ContextType.JavaScriptWithScope);
- State = BsonReaderState.ScopeDocument;
- return _currentValue.AsString;
- }
- /// <summary>
- /// Reads a BSON MaxKey from the reader.
- /// </summary>
- public override void ReadMaxKey()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadMaxKey", BsonType.MaxKey);
- State = GetNextState();
- }
- /// <summary>
- /// Reads a BSON MinKey from the reader.
- /// </summary>
- public override void ReadMinKey()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadMinKey", BsonType.MinKey);
- State = GetNextState();
- }
- /// <summary>
- /// Reads a BSON null from the reader.
- /// </summary>
- public override void ReadNull()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadNull", BsonType.Null);
- State = GetNextState();
- }
- /// <summary>
- /// Reads a BSON ObjectId from the reader.
- /// </summary>
- /// <returns>An ObjectId.</returns>
- public override ObjectId ReadObjectId()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadObjectId", BsonType.ObjectId);
- State = GetNextState();
- return _currentValue.AsObjectId;
- }
- /// <summary>
- /// Reads a BSON regular expression from the reader.
- /// </summary>
- /// <returns>A BsonRegularExpression.</returns>
- public override BsonRegularExpression ReadRegularExpression()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadRegularExpression", BsonType.RegularExpression);
- State = GetNextState();
- return _currentValue.AsBsonRegularExpression;
- }
- /// <summary>
- /// Reads the start of a BSON array.
- /// </summary>
- public override void ReadStartArray()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadStartArray", BsonType.Array);
- _context = new JsonReaderContext(_context, ContextType.Array);
- State = BsonReaderState.Type;
- }
- /// <summary>
- /// Reads the start of a BSON document.
- /// </summary>
- public override void ReadStartDocument()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadStartDocument", BsonType.Document);
- _context = new JsonReaderContext(_context, ContextType.Document);
- State = BsonReaderState.Type;
- }
- /// <summary>
- /// Reads a BSON string from the reader.
- /// </summary>
- /// <returns>A String.</returns>
- public override string ReadString()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadString", BsonType.String);
- State = GetNextState();
- return _currentValue.AsString;
- }
- /// <summary>
- /// Reads a BSON symbol from the reader.
- /// </summary>
- /// <returns>A string.</returns>
- public override string ReadSymbol()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadSymbol", BsonType.Symbol);
- State = GetNextState();
- return _currentValue.AsString;
- }
- /// <summary>
- /// Reads a BSON timestamp from the reader.
- /// </summary>
- /// <returns>The combined timestamp/increment.</returns>
- public override long ReadTimestamp()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadTimestamp", BsonType.Timestamp);
- State = GetNextState();
- var timestamp = _currentValue.AsBsonTimestamp;
- return timestamp.Value;
- }
- /// <summary>
- /// Reads a BSON undefined from the reader.
- /// </summary>
- public override void ReadUndefined()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- VerifyBsonType("ReadUndefined", BsonType.Undefined);
- State = GetNextState();
- }
- /// <summary>
- /// Returns the reader to previously bookmarked position and state.
- /// </summary>
- /// <param name="bookmark">The bookmark.</param>
- public override void ReturnToBookmark(BsonReaderBookmark bookmark)
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- var jsonReaderBookmark = (JsonReaderBookmark)bookmark;
- State = jsonReaderBookmark.State;
- CurrentBsonType = jsonReaderBookmark.CurrentBsonType;
- CurrentName = jsonReaderBookmark.CurrentName;
- _context = jsonReaderBookmark.CloneContext();
- _currentToken = jsonReaderBookmark.CurrentToken;
- _currentValue = jsonReaderBookmark.CurrentValue;
- _pushedToken = jsonReaderBookmark.PushedToken;
- _buffer.Position = jsonReaderBookmark.Position;
- }
- /// <summary>
- /// Skips the name (reader must be positioned on a name).
- /// </summary>
- public override void SkipName()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- if (State != BsonReaderState.Name)
- {
- ThrowInvalidState("SkipName", BsonReaderState.Name);
- }
- State = BsonReaderState.Value;
- }
- /// <summary>
- /// Skips the value (reader must be positioned on a value).
- /// </summary>
- public override void SkipValue()
- {
- if (Disposed) { ThrowObjectDisposedException(); }
- if (State != BsonReaderState.Value)
- {
- ThrowInvalidState("SkipValue", BsonReaderState.Value);
- }
- switch (CurrentBsonType)
- {
- case BsonType.Array:
- ReadStartArray();
- while (ReadBsonType() != BsonType.EndOfDocument)
- {
- SkipValue();
- }
- ReadEndArray();
- break;
- case BsonType.Binary:
- ReadBinaryData();
- break;
- case BsonType.Boolean:
- ReadBoolean();
- break;
- case BsonType.DateTime:
- ReadDateTime();
- break;
- case BsonType.Document:
- ReadStartDocument();
- while (ReadBsonType() != BsonType.EndOfDocument)
- {
- SkipName();
- SkipValue();
- }
- ReadEndDocument();
- break;
- case BsonType.Double:
- ReadDouble();
- break;
- case BsonType.Int32:
- ReadInt32();
- break;
- case BsonType.Int64:
- ReadInt64();
- break;
- case BsonType.JavaScript:
- ReadJavaScript();
- break;
- case BsonType.JavaScriptWithScope:
- ReadJavaScriptWithScope();
- ReadStartDocument();
- while (ReadBsonType() != BsonType.EndOfDocument)
- {
- SkipName();
- SkipValue();
- }
- ReadEndDocument();
- break;
- case BsonType.MaxKey:
- ReadMaxKey();
- break;
- case BsonType.MinKey:
- ReadMinKey();
- break;
- case BsonType.Null:
- ReadNull();
- break;
- case BsonType.ObjectId:
- ReadObjectId();
- break;
- case BsonType.RegularExpression:
- ReadRegularExpression();
- break;
- case BsonType.String:
- ReadString();
- break;
- case BsonType.Symbol:
- ReadSymbol();
- break;
- case BsonType.Timestamp:
- ReadTimestamp();
- break;
- case BsonType.Undefined:
- ReadUndefined();
- break;
- default:
- throw new BsonInternalException("Invalid BsonType.");
- }
- }
- // protected methods
- /// <summary>
- /// Disposes of any resources used by the reader.
- /// </summary>
- /// <param name="disposing">True if called from Dispose.</param>
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- try
- {
- Close();
- }
- catch { } // ignore exceptions
- }
- base.Dispose(disposing);
- }
- // private methods
- private string FormatInvalidTokenMessage(JsonToken token)
- {
- return string.Format("Invalid JSON token: '{0}'", token.Lexeme);
- }
- private string FormatJavaScriptDateTimeString(DateTime dateTime)
- {
- var utc = BsonUtils.ToUniversalTime(dateTime);
- var local = BsonUtils.ToLocalTime(utc);
- var offset = local - utc;
- var offsetSign = "+";
- if (offset < TimeSpan.Zero)
- {
- offset = -offset;
- offsetSign = "-";
- }
- var timeZone = TimeZone.CurrentTimeZone;
- var timeZoneName = local.IsDaylightSavingTime() ? timeZone.DaylightName : timeZone.StandardName;
- var dateTimeString = string.Format(
- "{0} GMT{1}{2:D2}{3:D2} ({4})",
- local.ToString("ddd MMM dd yyyy HH:mm:ss"), offsetSign, offset.Hours, offset.Minutes, timeZoneName);
- return dateTimeString;
- }
- private BsonReaderState GetNextState()
- {
- switch (_context.ContextType)
- {
- case ContextType.Array:
- case ContextType.Document:
- return BsonReaderState.Type;
- case ContextType.TopLevel:
- return BsonReaderState.Done;
- default:
- throw new BsonInternalException("Unexpected ContextType.");
- }
- }
- private BsonValue ParseBinDataConstructor()
- {
- VerifyToken("(");
- var subTypeToken = PopToken();
- if (subTypeToken.Type != JsonTokenType.Int32)
- {
- var message = string.Format("JSON reader expected a binary subtype but found '{0}'.", subTypeToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(",");
- var bytesToken = PopToken();
- if (bytesToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- var bytes = Convert.FromBase64String(bytesToken.StringValue);
- var subType = (BsonBinarySubType)subTypeToken.Int32Value;
- GuidRepresentation guidRepresentation;
- switch (subType)
- {
- case BsonBinarySubType.UuidLegacy: guidRepresentation = _jsonReaderSettings.GuidRepresentation; break;
- case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
- default: guidRepresentation = GuidRepresentation.Unspecified; break;
- }
- return new BsonBinaryData(bytes, subType, guidRepresentation);
- }
- private BsonValue ParseBinDataExtendedJson()
- {
- VerifyToken(":");
- var bytesToken = PopToken();
- if (bytesToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(",");
- VerifyString("$type");
- VerifyToken(":");
- var subTypeToken = PopToken();
- if (subTypeToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", subTypeToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- var bytes = Convert.FromBase64String(bytesToken.StringValue);
- var subType = (BsonBinarySubType)Convert.ToInt32(subTypeToken.StringValue, 16);
- GuidRepresentation guidRepresentation;
- switch (subType)
- {
- case BsonBinarySubType.UuidLegacy: guidRepresentation = _jsonReaderSettings.GuidRepresentation; break;
- case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
- default: guidRepresentation = GuidRepresentation.Unspecified; break;
- }
- return new BsonBinaryData(bytes, subType, guidRepresentation);
- }
- private BsonValue ParseHexDataConstructor()
- {
- VerifyToken("(");
- var subTypeToken = PopToken();
- if (subTypeToken.Type != JsonTokenType.Int32)
- {
- var message = string.Format("JSON reader expected a binary subtype but found '{0}'.", subTypeToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(",");
- var bytesToken = PopToken();
- if (bytesToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- var bytes = BsonUtils.ParseHexString(bytesToken.StringValue);
- var subType = (BsonBinarySubType)subTypeToken.Int32Value;
- GuidRepresentation guidRepresentation;
- switch (subType)
- {
- case BsonBinarySubType.UuidLegacy: guidRepresentation = _jsonReaderSettings.GuidRepresentation; break;
- case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
- default: guidRepresentation = GuidRepresentation.Unspecified; break;
- }
- return new BsonBinaryData(bytes, subType, guidRepresentation);
- }
- private BsonType ParseJavaScriptExtendedJson(out BsonValue value)
- {
- VerifyToken(":");
- var codeToken = PopToken();
- if (codeToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", codeToken.Lexeme);
- throw new Exception(message);
- }
- var nextToken = PopToken();
- switch (nextToken.Type)
- {
- case JsonTokenType.Comma:
- VerifyString("$scope");
- VerifyToken(":");
- State = BsonReaderState.Value;
- value = codeToken.StringValue;
- return BsonType.JavaScriptWithScope;
- case JsonTokenType.EndObject:
- value = codeToken.StringValue;
- return BsonType.JavaScript;
- default:
- var message = string.Format("JSON reader expected ',' or '}' but found '{0}'.", codeToken.Lexeme);
- throw new Exception(message);
- }
- }
- private BsonValue ParseISODateTimeConstructor()
- {
- VerifyToken("(");
- var valueToken = PopToken();
- if (valueToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- var formats = new string[] { "yyyy-MM-ddK", "yyyy-MM-ddTHH:mm:ssK", "yyyy-MM-ddTHH:mm:ss.FFFFFFFK" };
- var utcDateTime = DateTime.ParseExact(valueToken.StringValue, formats, null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
- return new BsonDateTime(utcDateTime);
- }
- private BsonValue ParseDateTimeExtendedJson()
- {
- VerifyToken(":");
- var valueToken = PopToken();
- long millisecondsSinceEpoch;
- if (valueToken.Type == JsonTokenType.Int32 || valueToken.Type == JsonTokenType.Int64)
- {
- millisecondsSinceEpoch = valueToken.Int64Value;
- }
- else if (valueToken.Type == JsonTokenType.String)
- {
- DateTime dateTime;
- if (!DateTime.TryParse(valueToken.StringValue, out dateTime))
- {
- var message = string.Format("Invalid $date string: '{0}'.", valueToken.StringValue);
- throw new Exception(message);
- }
- millisecondsSinceEpoch = BsonUtils.ToMillisecondsSinceEpoch(dateTime);
- }
- else
- {
- var message = string.Format("JSON reader expected an integer or an ISO 8601 string for $date but found a '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- return new BsonDateTime(millisecondsSinceEpoch);
- }
- private BsonValue ParseDateTimeConstructor(bool withNew)
- {
- VerifyToken("(");
- // Date when used without "new" behaves differently (JavaScript has some weird parts)
- if (!withNew)
- {
- VerifyToken(")");
- var dateTimeString = FormatJavaScriptDateTimeString(DateTime.UtcNow);
- return new BsonString(dateTimeString);
- }
- var token = PopToken();
- if (token.Lexeme == ")")
- {
- return new BsonDateTime(DateTime.UtcNow);
- }
- else if (token.Type == JsonTokenType.String)
- {
- VerifyToken(")");
- var dateTimeString = token.StringValue;
- var dateTime = ParseJavaScriptDateTimeString(dateTimeString);
- return new BsonDateTime(dateTime);
- }
- else if (token.Type == JsonTokenType.Int32 || token.Type == JsonTokenType.Int64)
- {
- var args = new List<long>();
- while (true)
- {
- args.Add(token.Int64Value);
- token = PopToken();
- if (token.Lexeme == ")")
- {
- break;
- }
- if (token.Lexeme != ",")
- {
- var message = string.Format("JSON reader expected a ',' or a ')' but found '{0}'.", token.Lexeme);
- throw new Exception(message);
- }
- token = PopToken();
- if (token.Type != JsonTokenType.Int32 && token.Type != JsonTokenType.Int64)
- {
- var message = string.Format("JSON reader expected an integer but found '{0}'.", token.Lexeme);
- throw new Exception(message);
- }
- }
- switch (args.Count)
- {
- case 1:
- return new BsonDateTime(args[0]);
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- var year = (int)args[0];
- var month = (int)args[1] + 1; // JavaScript starts at 0 but .NET starts at 1
- var day = (int)args[2];
- var hours = (args.Count >= 4) ? (int)args[3] : 0;
- var minutes = (args.Count >= 5) ? (int)args[4] : 0;
- var seconds = (args.Count >= 6) ? (int)args[5] : 0;
- var milliseconds = (args.Count == 7) ? (int)args[6] : 0;
- var dateTime = new DateTime(year, month, day, hours, minutes, seconds, milliseconds, DateTimeKind.Utc);
- return new BsonDateTime(dateTime);
- default:
- var message = string.Format("JSON reader expected 1 or 3-7 integers but found {0}.", args.Count);
- throw new Exception(message);
- }
- }
- else
- {
- var message = string.Format("JSON reader expected an integer or a string but found '{0}'.", token.Lexeme);
- throw new Exception(message);
- }
- }
- private BsonType ParseExtendedJson()
- {
- var nameToken = PopToken();
- if (nameToken.Type == JsonTokenType.String || nameToken.Type == JsonTokenType.UnquotedString)
- {
- switch (nameToken.StringValue)
- {
- case "$binary": _currentValue = ParseBinDataExtendedJson(); return BsonType.Binary;
- case "$code": return ParseJavaScriptExtendedJson(out _currentValue);
- case "$date": _currentValue = ParseDateTimeExtendedJson(); return BsonType.DateTime;
- case "$maxkey": case "$maxKey": _currentValue = ParseMaxKeyExtendedJson(); return BsonType.MaxKey;
- case "$minkey": case "$minKey": _currentValue = ParseMinKeyExtendedJson(); return BsonType.MinKey;
- case "$numberLong": _currentValue = ParseNumberLongExtendedJson(); return BsonType.Int64;
- case "$oid": _currentValue = ParseObjectIdExtendedJson(); return BsonType.ObjectId;
- case "$regex": _currentValue = ParseRegularExpressionExtendedJson(); return BsonType.RegularExpression;
- case "$symbol": _currentValue = ParseSymbolExtendedJson(); return BsonType.Symbol;
- case "$timestamp": _currentValue = ParseTimestampExtendedJson(); return BsonType.Timestamp;
- case "$undefined": _currentValue = ParseUndefinedExtendedJson(); return BsonType.Undefined;
- }
- }
- PushToken(nameToken);
- return BsonType.Document;
- }
- private DateTime ParseJavaScriptDateTimeString(string dateTimeString)
- {
- // if DateTime.TryParse succeeds we're done, otherwise assume it's an RFC 822 formatted DateTime string
- DateTime dateTime;
- if (DateTime.TryParse(dateTimeString, out dateTime))
- {
- return dateTime;
- }
- else
- {
- var rfc822DateTimePattern =
- @"^((?<dayOfWeek>(Mon|Tue|Wed|Thu|Fri|Sat|Sun)), )?" +
- @"(?<day>\d{1,2}) +" +
- @"(?<monthName>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " +
- @"(?<year>\d{2}|\d{4}) " +
- @"(?<hour>\d{1,2}):" +
- @"(?<minutes>\d{1,2}):" +
- @"(?<seconds>\d{1,2}(.\d{1,7})?) " +
- @"(?<zone>UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-Z]|([+-]\d{4}))$";
- var match = Regex.Match(dateTimeString, rfc822DateTimePattern);
- if (match.Success)
- {
- var day = int.Parse(match.Groups["day"].Value);
- int month;
- var monthName = match.Groups["monthName"].Value;
- switch (monthName)
- {
- case "Jan": month = 1; break;
- case "Feb": month = 2; break;
- case "Mar": month = 3; break;
- case "Apr": month = 4; break;
- case "May": month = 5; break;
- case "Jun": month = 6; break;
- case "Jul": month = 7; break;
- case "Aug": month = 8; break;
- case "Sep": month = 9; break;
- case "Oct": month = 10; break;
- case "Nov": month = 11; break;
- case "Dec": month = 12; break;
- default:
- var message = string.Format("\"{0}\" is not a valid RFC 822 month name.", monthName);
- throw new Exception(message);
- }
- var yearString = match.Groups["year"].Value;
- int year = int.Parse(yearString);
- if (yearString.Length == 2)
- {
- year += 2000;
- if (year - DateTime.UtcNow.Year >= 19) { year -= 100; }
- }
- var hour = int.Parse(match.Groups["hour"].Value);
- var minutes = int.Parse(match.Groups["minutes"].Value);
- var secondsString = match.Groups["seconds"].Value;
- int seconds;
- double milliseconds;
- if (secondsString.IndexOf('.') != -1)
- {
- var timeSpan = TimeSpan.FromSeconds(double.Parse(secondsString));
- seconds = timeSpan.Seconds;
- milliseconds = timeSpan.TotalMilliseconds - seconds * 1000;
- }
- else
- {
- seconds = int.Parse(secondsString);
- milliseconds = 0;
- }
- dateTime = new DateTime(year, month, day, hour, minutes, seconds, DateTimeKind.Utc).AddMilliseconds(milliseconds);
- // check day of week before converting to UTC
- var dayOfWeekString = match.Groups["dayOfWeek"].Value;
- if (dayOfWeekString != "")
- {
- DayOfWeek dayOfWeek;
- switch (dayOfWeekString)
- {
- case "Mon": dayOfWeek = DayOfWeek.Monday; break;
- case "Tue": dayOfWeek = DayOfWeek.Tuesday; break;
- case "Wed": dayOfWeek = DayOfWeek.Wednesday; break;
- case "Thu": dayOfWeek = DayOfWeek.Thursday; break;
- case "Fri": dayOfWeek = DayOfWeek.Friday; break;
- case "Sat": dayOfWeek = DayOfWeek.Saturday; break;
- case "Sun": dayOfWeek = DayOfWeek.Sunday; break;
- default:
- var message = string.Format("\"{0}\" is not a valid RFC 822 day name.", dayOfWeekString);
- throw new Exception(message);
- }
- if (dateTime.DayOfWeek != dayOfWeek)
- {
- var message = string.Format("\"{0}\" is not the right day of the week for {1}.", dayOfWeekString, dateTime.ToString("o"));
- throw new Exception(message);
- }
- }
- TimeSpan offset;
- var zone = match.Groups["zone"].Value;
- switch (zone)
- {
- case "UT": case "GMT": case "Z": offset = TimeSpan.Zero; break;
- case "EST": offset = TimeSpan.FromHours(-5); break;
- case "EDT": offset = TimeSpan.FromHours(-4); break;
- case "CST": offset = TimeSpan.FromHours(-6); break;
- case "CDT": offset = TimeSpan.FromHours(-5); break;
- case "MST": offset = TimeSpan.FromHours(-7); break;
- case "MDT": offset = TimeSpan.FromHours(-6); break;
- case "PST": offset = TimeSpan.FromHours(-8); break;
- case "PDT": offset = TimeSpan.FromHours(-7); break;
- case "A": offset = TimeSpan.FromHours(-1); break;
- case "B": offset = TimeSpan.FromHours(-2); break;
- case "C": offset = TimeSpan.FromHours(-3); break;
- case "D": offset = TimeSpan.FromHours(-4); break;
- case "E": offset = TimeSpan.FromHours(-5); break;
- case "F": offset = TimeSpan.FromHours(-6); break;
- case "G": offset = TimeSpan.FromHours(-7); break;
- case "H": offset = TimeSpan.FromHours(-8); break;
- case "I": offset = TimeSpan.FromHours(-9); break;
- case "K": offset = TimeSpan.FromHours(-10); break;
- case "L": offset = TimeSpan.FromHours(-11); break;
- case "M": offset = TimeSpan.FromHours(-12); break;
- case "N": offset = TimeSpan.FromHours(1); break;
- case "O": offset = TimeSpan.FromHours(2); break;
- case "P": offset = TimeSpan.FromHours(3); break;
- case "Q": offset = TimeSpan.FromHours(4); break;
- case "R": offset = TimeSpan.FromHours(5); break;
- case "S": offset = TimeSpan.FromHours(6); break;
- case "T": offset = TimeSpan.FromHours(7); break;
- case "U": offset = TimeSpan.FromHours(8); break;
- case "V": offset = TimeSpan.FromHours(9); break;
- case "W": offset = TimeSpan.FromHours(10); break;
- case "X": offset = TimeSpan.FromHours(11); break;
- case "Y": offset = TimeSpan.FromHours(12); break;
- default:
- var offsetSign = zone.Substring(0);
- var offsetHours = zone.Substring(1, 2);
- var offsetMinutes = zone.Substring(3, 2);
- offset = TimeSpan.FromHours(int.Parse(offsetHours)) + TimeSpan.FromMinutes(int.Parse(offsetMinutes));
- if (offsetSign == "-")
- {
- offset = -offset;
- }
- break;
- }
- return dateTime.Add(-offset);
- }
- else
- {
- var message = string.Format("The DateTime string \"{0}\" is not a valid DateTime string for either .NET or JavaScript.", dateTimeString);
- throw new Exception(message);
- }
- }
- }
- private BsonValue ParseMaxKeyExtendedJson()
- {
- VerifyToken(":");
- VerifyToken("1");
- VerifyToken("}");
- return BsonMaxKey.Value;
- }
- private BsonValue ParseMinKeyExtendedJson()
- {
- VerifyToken(":");
- VerifyToken("1");
- VerifyToken("}");
- return BsonMinKey.Value;
- }
- private BsonType ParseNew(out BsonValue value)
- {
- var typeToken = PopToken();
- if (typeToken.Type != JsonTokenType.UnquotedString)
- {
- var message = string.Format("JSON reader expected a type name but found '{0}'.", typeToken.Lexeme);
- throw new Exception(message);
- }
- switch (typeToken.Lexeme)
- {
- case "BinData":
- value = ParseBinDataConstructor();
- return BsonType.Binary;
- case "Date":
- value = ParseDateTimeConstructor(true); // withNew = true
- return BsonType.DateTime;
- case "HexData":
- value = ParseHexDataConstructor();
- return BsonType.Binary;
- case "ISODate":
- value = ParseISODateTimeConstructor();
- return BsonType.DateTime;
- case "NumberInt":
- value = ParseNumberConstructor();
- return BsonType.Int32;
- case "NumberLong":
- value = ParseNumberLongConstructor();
- return BsonType.Int64;
- case "ObjectId":
- value = ParseObjectIdConstructor();
- return BsonType.ObjectId;
- case "RegExp":
- value = ParseRegularExpressionConstructor();
- return BsonType.RegularExpression;
- case "Timestamp":
- value = ParseTimestampConstructor();
- return BsonType.Timestamp;
- case "UUID":
- case "GUID":
- case "CSUUID":
- case "CSGUID":
- case "JUUID":
- case "JGUID":
- case "PYUUID":
- case "PYGUID":
- value = ParseUUIDConstructor(typeToken.Lexeme);
- return BsonType.Binary;
- default:
- var message = string.Format("JSON reader expected a type name but found '{0}'.", typeToken.Lexeme);
- throw new Exception(message);
- }
- }
- private BsonValue ParseNumberConstructor()
- {
- VerifyToken("(");
- var valueToken = PopToken();
- int value;
- if (valueToken.IsNumber)
- {
- value = valueToken.Int32Value;
- }
- else if (valueToken.Type == JsonTokenType.String)
- {
- value = int.Parse(valueToken.StringValue);
- }
- else
- {
- var message = string.Format("JSON reader expected an integer or a string but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- return new BsonInt32(value);
- }
- private BsonValue ParseNumberLongConstructor()
- {
- VerifyToken("(");
- var valueToken = PopToken();
- long value;
- if (valueToken.Type == JsonTokenType.Int32 || valueToken.Type == JsonTokenType.Int64)
- {
- value = valueToken.Int64Value;
- }
- else if (valueToken.Type == JsonTokenType.String)
- {
- value = long.Parse(valueToken.StringValue);
- }
- else
- {
- var message = string.Format("JSON reader expected an integer or a string but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- return new BsonInt64(value);
- }
- private BsonValue ParseNumberLongExtendedJson()
- {
- VerifyToken(":");
- var valueToken = PopToken();
- if (valueToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- return new BsonInt64(long.Parse(valueToken.StringValue));
- }
- private BsonValue ParseObjectIdConstructor()
- {
- VerifyToken("(");
- var valueToken = PopToken();
- if (valueToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- return new BsonObjectId(ObjectId.Parse(valueToken.StringValue));
- }
- private BsonValue ParseObjectIdExtendedJson()
- {
- VerifyToken(":");
- var valueToken = PopToken();
- if (valueToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- return new BsonObjectId(ObjectId.Parse(valueToken.StringValue));
- }
- private BsonValue ParseRegularExpressionConstructor()
- {
- VerifyToken("(");
- var patternToken = PopToken();
- if (patternToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme);
- throw new Exception(message);
- }
- var options = "";
- var commaToken = PopToken();
- if (commaToken.Lexeme == ",")
- {
- var optionsToken = PopToken();
- if (optionsToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme);
- throw new Exception(message);
- }
- options = optionsToken.StringValue;
- }
- else
- {
- PushToken(commaToken);
- }
- VerifyToken(")");
- return new BsonRegularExpression(patternToken.StringValue, options);
- }
- private BsonValue ParseRegularExpressionExtendedJson()
- {
- VerifyToken(":");
- var patternToken = PopToken();
- if (patternToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme);
- throw new Exception(message);
- }
- var options = "";
- var commaToken = PopToken();
- if (commaToken.Lexeme == ",")
- {
- VerifyString("$options");
- VerifyToken(":");
- var optionsToken = PopToken();
- if (optionsToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme);
- throw new Exception(message);
- }
- options = optionsToken.StringValue;
- }
- else
- {
- PushToken(commaToken);
- }
- VerifyToken("}");
- return new BsonRegularExpression(patternToken.StringValue, options);
- }
- private BsonValue ParseSymbolExtendedJson()
- {
- VerifyToken(":");
- var nameToken = PopToken();
- if (nameToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", nameToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- return new BsonString(nameToken.StringValue); // will be converted to a BsonSymbol at a higher level
- }
- private BsonValue ParseTimestampConstructor()
- {
- VerifyToken("(");
- int secondsSinceEpoch;
- var secondsSinceEpochToken = PopToken();
- if (secondsSinceEpochToken.IsNumber)
- {
- secondsSinceEpoch = secondsSinceEpochToken.Int32Value;
- }
- else
- {
- var message = string.Format("JSON reader expected a number but found '{0}'.", secondsSinceEpochToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(",");
- int increment;
- var incrementToken = PopToken();
- if (secondsSinceEpochToken.IsNumber)
- {
- increment = incrementToken.Int32Value;
- }
- else
- {
- var message = string.Format("JSON reader expected a number but found '{0}'.", secondsSinceEpochToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- return new BsonTimestamp(secondsSinceEpoch, increment);
- }
- private BsonValue ParseTimestampExtendedJson()
- {
- VerifyToken(":");
- var nextToken = PopToken();
- if (nextToken.Type == JsonTokenType.BeginObject)
- {
- return ParseTimestampExtendedJsonNewRepresentation();
- }
- else
- {
- return ParseTimestampExtendedJsonOldRepresentation(nextToken);
- }
- }
- private BsonValue ParseTimestampExtendedJsonNewRepresentation()
- {
- VerifyString("t");
- VerifyToken(":");
- var secondsSinceEpochToken = PopToken();
- int secondsSinceEpoch;
- if (secondsSinceEpochToken.IsNumber)
- {
- secondsSinceEpoch = secondsSinceEpochToken.Int32Value;
- }
- else
- {
- var message = string.Format("JSON reader expected an integer but found '{0}'.", secondsSinceEpochToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(",");
- VerifyString("i");
- VerifyToken(":");
- var incrementToken = PopToken();
- int increment;
- if (incrementToken.IsNumber)
- {
- increment = incrementToken.Int32Value;
- }
- else
- {
- var message = string.Format("JSON reader expected an integer but found '{0}'.", incrementToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- VerifyToken("}");
- return new BsonTimestamp(secondsSinceEpoch, increment);
- }
- private BsonValue ParseTimestampExtendedJsonOldRepresentation(JsonToken valueToken)
- {
- long value;
- if (valueToken.Type == JsonTokenType.Int32 || valueToken.Type == JsonTokenType.Int64)
- {
- value = valueToken.Int64Value;
- }
- else if (valueToken.Type == JsonTokenType.UnquotedString && valueToken.Lexeme == "NumberLong")
- {
- value = ParseNumberLongConstructor().AsInt64;
- }
- else
- {
- var message = string.Format("JSON reader expected an integer but found '{0}'.", valueToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken("}");
- return new BsonTimestamp(value);
- }
- private BsonValue ParseUndefinedExtendedJson()
- {
- VerifyToken(":");
- VerifyToken("true");
- VerifyToken("}");
- return BsonMaxKey.Value;
- }
- private BsonValue ParseUUIDConstructor(string uuidConstructorName)
- {
- VerifyToken("(");
- var bytesToken = PopToken();
- if (bytesToken.Type != JsonTokenType.String)
- {
- var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
- throw new Exception(message);
- }
- VerifyToken(")");
- var hexString = bytesToken.StringValue.Replace("{", "").Replace("}", "").Replace("-", "");
- var bytes = BsonUtils.ParseHexString(hexString);
- var guid = GuidConverter.FromBytes(bytes, GuidRepresentation.Standard);
- GuidRepresentation guidRepresentation;
- switch (uuidConstructorName)
- {
- case "CSUUID":
- case "CSGUID":
- guidRepresentation = GuidRepresentation.CSharpLegacy;
- break;
- case "JUUID":
- case "JGUID":
- guidRepresentation = GuidRepresentation.JavaLegacy;
- break;
- case "PYUUID":
- case "PYGUID":
- guidRepresentation = GuidRepresentation.PythonLegacy;
- break;
- case "UUID":
- case "GUID":
- guidRepresentation = GuidRepresentation.Standard;
- break;
- default:
- throw new BsonInternalException("Unexpected uuidConstructorName");
- }
- bytes = GuidConverter.ToBytes(guid, guidRepresentation);
- var subType = (guidRepresentation == GuidRepresentation.Standard) ? BsonBinarySubType.UuidStandard : BsonBinarySubType.UuidLegacy;
- return new BsonBinaryData(bytes, subType, guidRepresentation);
- }
- private JsonToken PopToken()
- {
- if (_pushedToken != null)
- {
- var token = _pushedToken;
- _pushedToken = null;
- return token;
- }
- else
- {
- return JsonScanner.GetNextToken(_buffer);
- }
- }
- private void PushToken(JsonToken token)
- {
- if (_pushedToken == null)
- {
- _pushedToken = token;
- }
- else
- {
- throw new BsonInternalException("There is already a pending token.");
- }
- }
- private void VerifyString(string expectedString)
- {
- var token = PopToken();
- if ((token.Type != JsonTokenType.String && token.Type != JsonTokenType.UnquotedString) || token.StringValue != expectedString)
- {
- var message = string.Format("JSON reader expected '{0}' but found '{1}'.", expectedString, token.StringValue);
- throw new Exception(message);
- }
- }
- private void VerifyToken(string expectedLexeme)
- {
- var token = PopToken();
- if (token.Lexeme != expectedLexeme)
- {
- var message = string.Format("JSON reader expected '{0}' but found '{1}'.", expectedLexeme, token.Lexeme);
- throw new Exception(message);
- }
- }
- }
- }
|