JsonReader.cs 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643
  1. /* Copyright 2010-2014 MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Globalization;
  18. using System.IO;
  19. using System.Text.RegularExpressions;
  20. using System.Xml;
  21. namespace MongoDB.Bson.IO
  22. {
  23. /// <summary>
  24. /// Represents a BSON reader for a JSON string.
  25. /// </summary>
  26. public class JsonReader : BsonReader
  27. {
  28. // private fields
  29. private JsonBuffer _buffer;
  30. private JsonReaderSettings _jsonReaderSettings; // same value as in base class just declared as derived class
  31. private JsonReaderContext _context;
  32. private JsonToken _currentToken;
  33. private BsonValue _currentValue;
  34. private JsonToken _pushedToken;
  35. // constructors
  36. /// <summary>
  37. /// Initializes a new instance of the JsonReader class.
  38. /// </summary>
  39. /// <param name="buffer">The buffer.</param>
  40. /// <param name="settings">The reader settings.</param>
  41. public JsonReader(JsonBuffer buffer, JsonReaderSettings settings)
  42. : base(settings)
  43. {
  44. if (buffer == null)
  45. {
  46. throw new ArgumentNullException("buffer");
  47. }
  48. _buffer = buffer;
  49. _jsonReaderSettings = settings; // already frozen by base class
  50. _context = new JsonReaderContext(null, ContextType.TopLevel);
  51. }
  52. // public methods
  53. /// <summary>
  54. /// Closes the reader.
  55. /// </summary>
  56. public override void Close()
  57. {
  58. // Close can be called on Disposed objects
  59. if (State != BsonReaderState.Closed)
  60. {
  61. State = BsonReaderState.Closed;
  62. }
  63. }
  64. /// <summary>
  65. /// Gets a bookmark to the reader's current position and state.
  66. /// </summary>
  67. /// <returns>A bookmark.</returns>
  68. public override BsonReaderBookmark GetBookmark()
  69. {
  70. return new JsonReaderBookmark(State, CurrentBsonType, CurrentName, _context, _currentToken, _currentValue, _pushedToken, _buffer.Position);
  71. }
  72. /// <summary>
  73. /// Reads BSON binary data from the reader.
  74. /// </summary>
  75. /// <returns>A BsonBinaryData.</returns>
  76. public override BsonBinaryData ReadBinaryData()
  77. {
  78. if (Disposed) { ThrowObjectDisposedException(); }
  79. VerifyBsonType("ReadBinaryData", BsonType.Binary);
  80. State = GetNextState();
  81. return _currentValue.AsBsonBinaryData;
  82. }
  83. /// <summary>
  84. /// Reads a BSON boolean from the reader.
  85. /// </summary>
  86. /// <returns>A Boolean.</returns>
  87. public override bool ReadBoolean()
  88. {
  89. if (Disposed) { ThrowObjectDisposedException(); }
  90. VerifyBsonType("ReadBoolean", BsonType.Boolean);
  91. State = GetNextState();
  92. return _currentValue.AsBoolean;
  93. }
  94. /// <summary>
  95. /// Reads a BsonType from the reader.
  96. /// </summary>
  97. /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
  98. /// <param name="bsonTrie">An optional trie to search for a value that matches the next element name.</param>
  99. /// <param name="found">Set to true if a matching value was found in the trie.</param>
  100. /// <param name="value">Set to the matching value found in the trie or null if no matching value was found.</param>
  101. /// <returns>A BsonType.</returns>
  102. public override BsonType ReadBsonType<TValue>(BsonTrie<TValue> bsonTrie, out bool found, out TValue value)
  103. {
  104. if (Disposed) { ThrowObjectDisposedException(); }
  105. found = false;
  106. value = default(TValue);
  107. if (State == BsonReaderState.Initial || State == BsonReaderState.Done || State == BsonReaderState.ScopeDocument)
  108. {
  109. // in JSON the top level value can be of any type so fall through
  110. State = BsonReaderState.Type;
  111. }
  112. if (State != BsonReaderState.Type)
  113. {
  114. ThrowInvalidState("ReadBsonType", BsonReaderState.Type);
  115. }
  116. if (_context.ContextType == ContextType.Document)
  117. {
  118. var nameToken = PopToken();
  119. switch (nameToken.Type)
  120. {
  121. case JsonTokenType.String:
  122. case JsonTokenType.UnquotedString:
  123. if (bsonTrie != null)
  124. {
  125. found = bsonTrie.TryGetValue(nameToken.StringValue, out value);
  126. }
  127. CurrentName = nameToken.StringValue;
  128. break;
  129. case JsonTokenType.EndObject:
  130. State = BsonReaderState.EndOfDocument;
  131. return BsonType.EndOfDocument;
  132. default:
  133. var message = string.Format("JSON reader was expecting a name but found '{0}'.", nameToken.Lexeme);
  134. throw new Exception(message);
  135. }
  136. var colonToken = PopToken();
  137. if (colonToken.Type != JsonTokenType.Colon)
  138. {
  139. var message = string.Format("JSON reader was expecting ':' but found '{0}'.", colonToken.Lexeme);
  140. throw new Exception(message);
  141. }
  142. }
  143. var valueToken = PopToken();
  144. if (_context.ContextType == ContextType.Array && valueToken.Type == JsonTokenType.EndArray)
  145. {
  146. State = BsonReaderState.EndOfArray;
  147. return BsonType.EndOfDocument;
  148. }
  149. var noValueFound = false;
  150. switch (valueToken.Type)
  151. {
  152. case JsonTokenType.BeginArray:
  153. CurrentBsonType = BsonType.Array;
  154. break;
  155. case JsonTokenType.BeginObject:
  156. CurrentBsonType = ParseExtendedJson();
  157. break;
  158. case JsonTokenType.DateTime:
  159. CurrentBsonType = BsonType.DateTime;
  160. _currentValue = valueToken.DateTimeValue;
  161. break;
  162. case JsonTokenType.Double:
  163. CurrentBsonType = BsonType.Double;
  164. _currentValue = valueToken.DoubleValue;
  165. break;
  166. case JsonTokenType.EndOfFile:
  167. CurrentBsonType = BsonType.EndOfDocument;
  168. break;
  169. case JsonTokenType.Int32:
  170. CurrentBsonType = BsonType.Int32;
  171. _currentValue = valueToken.Int32Value;
  172. break;
  173. case JsonTokenType.Int64:
  174. CurrentBsonType = BsonType.Int64;
  175. _currentValue = valueToken.Int64Value;
  176. break;
  177. case JsonTokenType.ObjectId:
  178. CurrentBsonType = BsonType.ObjectId;
  179. _currentValue = valueToken.ObjectIdValue;
  180. break;
  181. case JsonTokenType.RegularExpression:
  182. CurrentBsonType = BsonType.RegularExpression;
  183. _currentValue = valueToken.RegularExpressionValue;
  184. break;
  185. case JsonTokenType.String:
  186. CurrentBsonType = BsonType.String;
  187. _currentValue = valueToken.StringValue;
  188. break;
  189. case JsonTokenType.UnquotedString:
  190. switch (valueToken.Lexeme)
  191. {
  192. case "false":
  193. case "true":
  194. CurrentBsonType = BsonType.Boolean;
  195. _currentValue = XmlConvert.ToBoolean(valueToken.Lexeme);
  196. break;
  197. case "Infinity":
  198. CurrentBsonType = BsonType.Double;
  199. _currentValue = double.PositiveInfinity;
  200. break;
  201. case "NaN":
  202. CurrentBsonType = BsonType.Double;
  203. _currentValue = double.NaN;
  204. break;
  205. case "null":
  206. CurrentBsonType = BsonType.Null;
  207. break;
  208. case "undefined":
  209. CurrentBsonType = BsonType.Undefined;
  210. break;
  211. case "BinData":
  212. CurrentBsonType = BsonType.Binary;
  213. _currentValue = ParseBinDataConstructor();
  214. break;
  215. case "Date":
  216. CurrentBsonType = BsonType.String;
  217. _currentValue = ParseDateTimeConstructor(false); // withNew = false
  218. break;
  219. case "HexData":
  220. CurrentBsonType = BsonType.Binary;
  221. _currentValue = ParseHexDataConstructor();
  222. break;
  223. case "ISODate":
  224. CurrentBsonType = BsonType.DateTime;
  225. _currentValue = ParseISODateTimeConstructor();
  226. break;
  227. case "MaxKey":
  228. CurrentBsonType = BsonType.MaxKey;
  229. _currentValue = BsonMaxKey.Value;
  230. break;
  231. case "MinKey":
  232. CurrentBsonType = BsonType.MinKey;
  233. _currentValue = BsonMinKey.Value;
  234. break;
  235. case "Number":
  236. case "NumberInt":
  237. CurrentBsonType = BsonType.Int32;
  238. _currentValue = ParseNumberConstructor();
  239. break;
  240. case "NumberLong":
  241. CurrentBsonType = BsonType.Int64;
  242. _currentValue = ParseNumberLongConstructor();
  243. break;
  244. case "ObjectId":
  245. CurrentBsonType = BsonType.ObjectId;
  246. _currentValue = ParseObjectIdConstructor();
  247. break;
  248. case "RegExp":
  249. CurrentBsonType = BsonType.RegularExpression;
  250. _currentValue = ParseRegularExpressionConstructor();
  251. break;
  252. case "Timestamp":
  253. CurrentBsonType = BsonType.Timestamp;
  254. _currentValue = ParseTimestampConstructor();
  255. break;
  256. case "UUID":
  257. case "GUID":
  258. case "CSUUID":
  259. case "CSGUID":
  260. case "JUUID":
  261. case "JGUID":
  262. case "PYUUID":
  263. case "PYGUID":
  264. CurrentBsonType = BsonType.Binary;
  265. _currentValue = ParseUUIDConstructor(valueToken.Lexeme);
  266. break;
  267. case "new":
  268. CurrentBsonType = ParseNew(out _currentValue);
  269. break;
  270. default:
  271. noValueFound = true;
  272. break;
  273. }
  274. break;
  275. default:
  276. noValueFound = true;
  277. break;
  278. }
  279. if (noValueFound)
  280. {
  281. var message = string.Format("JSON reader was expecting a value but found '{0}'.", valueToken.Lexeme);
  282. throw new Exception(message);
  283. }
  284. _currentToken = valueToken;
  285. if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.Document)
  286. {
  287. var commaToken = PopToken();
  288. if (commaToken.Type != JsonTokenType.Comma)
  289. {
  290. PushToken(commaToken);
  291. }
  292. }
  293. switch (_context.ContextType)
  294. {
  295. case ContextType.Document:
  296. case ContextType.ScopeDocument:
  297. default:
  298. State = BsonReaderState.Name;
  299. break;
  300. case ContextType.Array:
  301. case ContextType.JavaScriptWithScope:
  302. case ContextType.TopLevel:
  303. State = BsonReaderState.Value;
  304. break;
  305. }
  306. return CurrentBsonType;
  307. }
  308. /// <summary>
  309. /// Reads BSON binary data from the reader.
  310. /// </summary>
  311. /// <returns>A byte array.</returns>
  312. public override byte[] ReadBytes()
  313. {
  314. #pragma warning disable 618
  315. if (Disposed) { ThrowObjectDisposedException(); }
  316. VerifyBsonType("ReadBinaryData", BsonType.Binary);
  317. State = GetNextState();
  318. var binaryData = _currentValue.AsBsonBinaryData;
  319. var subType = binaryData.SubType;
  320. if (subType != BsonBinarySubType.Binary && subType != BsonBinarySubType.OldBinary)
  321. {
  322. var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {2}.", subType);
  323. throw new Exception(message);
  324. }
  325. return binaryData.Bytes;
  326. #pragma warning restore
  327. }
  328. /// <summary>
  329. /// Reads a BSON DateTime from the reader.
  330. /// </summary>
  331. /// <returns>The number of milliseconds since the Unix epoch.</returns>
  332. public override long ReadDateTime()
  333. {
  334. if (Disposed) { ThrowObjectDisposedException(); }
  335. VerifyBsonType("ReadDateTime", BsonType.DateTime);
  336. State = GetNextState();
  337. return _currentValue.AsBsonDateTime.MillisecondsSinceEpoch;
  338. }
  339. /// <summary>
  340. /// Reads a BSON Double from the reader.
  341. /// </summary>
  342. /// <returns>A Double.</returns>
  343. public override double ReadDouble()
  344. {
  345. if (Disposed) { ThrowObjectDisposedException(); }
  346. VerifyBsonType("ReadDouble", BsonType.Double);
  347. State = GetNextState();
  348. return _currentValue.AsDouble;
  349. }
  350. /// <summary>
  351. /// Reads the end of a BSON array from the reader.
  352. /// </summary>
  353. public override void ReadEndArray()
  354. {
  355. if (Disposed) { ThrowObjectDisposedException(); }
  356. if (_context.ContextType != ContextType.Array)
  357. {
  358. ThrowInvalidContextType("ReadEndArray", _context.ContextType, ContextType.Array);
  359. }
  360. if (State == BsonReaderState.Type)
  361. {
  362. ReadBsonType(); // will set state to EndOfArray if at end of array
  363. }
  364. if (State != BsonReaderState.EndOfArray)
  365. {
  366. ThrowInvalidState("ReadEndArray", BsonReaderState.EndOfArray);
  367. }
  368. _context = _context.PopContext();
  369. switch (_context.ContextType)
  370. {
  371. case ContextType.Array: State = BsonReaderState.Type; break;
  372. case ContextType.Document: State = BsonReaderState.Type; break;
  373. case ContextType.TopLevel: State = BsonReaderState.Done; break;
  374. default: throw new BsonInternalException("Unexpected ContextType.");
  375. }
  376. if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.Document)
  377. {
  378. var commaToken = PopToken();
  379. if (commaToken.Type != JsonTokenType.Comma)
  380. {
  381. PushToken(commaToken);
  382. }
  383. }
  384. }
  385. /// <summary>
  386. /// Reads the end of a BSON document from the reader.
  387. /// </summary>
  388. public override void ReadEndDocument()
  389. {
  390. if (Disposed) { ThrowObjectDisposedException(); }
  391. if (_context.ContextType != ContextType.Document && _context.ContextType != ContextType.ScopeDocument)
  392. {
  393. ThrowInvalidContextType("ReadEndDocument", _context.ContextType, ContextType.Document, ContextType.ScopeDocument);
  394. }
  395. if (State == BsonReaderState.Type)
  396. {
  397. ReadBsonType(); // will set state to EndOfDocument if at end of document
  398. }
  399. if (State != BsonReaderState.EndOfDocument)
  400. {
  401. ThrowInvalidState("ReadEndDocument", BsonReaderState.EndOfDocument);
  402. }
  403. _context = _context.PopContext();
  404. if (_context != null && _context.ContextType == ContextType.JavaScriptWithScope)
  405. {
  406. _context = _context.PopContext(); // JavaScriptWithScope
  407. VerifyToken("}"); // outermost closing bracket for JavaScriptWithScope
  408. }
  409. switch (_context.ContextType)
  410. {
  411. case ContextType.Array: State = BsonReaderState.Type; break;
  412. case ContextType.Document: State = BsonReaderState.Type; break;
  413. case ContextType.TopLevel: State = BsonReaderState.Done; break;
  414. default: throw new BsonInternalException("Unexpected ContextType");
  415. }
  416. if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.Document)
  417. {
  418. var commaToken = PopToken();
  419. if (commaToken.Type != JsonTokenType.Comma)
  420. {
  421. PushToken(commaToken);
  422. }
  423. }
  424. }
  425. /// <summary>
  426. /// Reads a BSON Int32 from the reader.
  427. /// </summary>
  428. /// <returns>An Int32.</returns>
  429. public override int ReadInt32()
  430. {
  431. if (Disposed) { ThrowObjectDisposedException(); }
  432. VerifyBsonType("ReadInt32", BsonType.Int32);
  433. State = GetNextState();
  434. return _currentValue.AsInt32;
  435. }
  436. /// <summary>
  437. /// Reads a BSON Int64 from the reader.
  438. /// </summary>
  439. /// <returns>An Int64.</returns>
  440. public override long ReadInt64()
  441. {
  442. if (Disposed) { ThrowObjectDisposedException(); }
  443. VerifyBsonType("ReadInt64", BsonType.Int64);
  444. State = GetNextState();
  445. return _currentValue.AsInt64;
  446. }
  447. /// <summary>
  448. /// Reads a BSON JavaScript from the reader.
  449. /// </summary>
  450. /// <returns>A string.</returns>
  451. public override string ReadJavaScript()
  452. {
  453. if (Disposed) { ThrowObjectDisposedException(); }
  454. VerifyBsonType("ReadJavaScript", BsonType.JavaScript);
  455. State = GetNextState();
  456. return _currentValue.AsString;
  457. }
  458. /// <summary>
  459. /// Reads a BSON JavaScript with scope from the reader (call ReadStartDocument next to read the scope).
  460. /// </summary>
  461. /// <returns>A string.</returns>
  462. public override string ReadJavaScriptWithScope()
  463. {
  464. if (Disposed) { ThrowObjectDisposedException(); }
  465. VerifyBsonType("ReadJavaScriptWithScope", BsonType.JavaScriptWithScope);
  466. _context = new JsonReaderContext(_context, ContextType.JavaScriptWithScope);
  467. State = BsonReaderState.ScopeDocument;
  468. return _currentValue.AsString;
  469. }
  470. /// <summary>
  471. /// Reads a BSON MaxKey from the reader.
  472. /// </summary>
  473. public override void ReadMaxKey()
  474. {
  475. if (Disposed) { ThrowObjectDisposedException(); }
  476. VerifyBsonType("ReadMaxKey", BsonType.MaxKey);
  477. State = GetNextState();
  478. }
  479. /// <summary>
  480. /// Reads a BSON MinKey from the reader.
  481. /// </summary>
  482. public override void ReadMinKey()
  483. {
  484. if (Disposed) { ThrowObjectDisposedException(); }
  485. VerifyBsonType("ReadMinKey", BsonType.MinKey);
  486. State = GetNextState();
  487. }
  488. /// <summary>
  489. /// Reads a BSON null from the reader.
  490. /// </summary>
  491. public override void ReadNull()
  492. {
  493. if (Disposed) { ThrowObjectDisposedException(); }
  494. VerifyBsonType("ReadNull", BsonType.Null);
  495. State = GetNextState();
  496. }
  497. /// <summary>
  498. /// Reads a BSON ObjectId from the reader.
  499. /// </summary>
  500. /// <returns>An ObjectId.</returns>
  501. public override ObjectId ReadObjectId()
  502. {
  503. if (Disposed) { ThrowObjectDisposedException(); }
  504. VerifyBsonType("ReadObjectId", BsonType.ObjectId);
  505. State = GetNextState();
  506. return _currentValue.AsObjectId;
  507. }
  508. /// <summary>
  509. /// Reads a BSON regular expression from the reader.
  510. /// </summary>
  511. /// <returns>A BsonRegularExpression.</returns>
  512. public override BsonRegularExpression ReadRegularExpression()
  513. {
  514. if (Disposed) { ThrowObjectDisposedException(); }
  515. VerifyBsonType("ReadRegularExpression", BsonType.RegularExpression);
  516. State = GetNextState();
  517. return _currentValue.AsBsonRegularExpression;
  518. }
  519. /// <summary>
  520. /// Reads the start of a BSON array.
  521. /// </summary>
  522. public override void ReadStartArray()
  523. {
  524. if (Disposed) { ThrowObjectDisposedException(); }
  525. VerifyBsonType("ReadStartArray", BsonType.Array);
  526. _context = new JsonReaderContext(_context, ContextType.Array);
  527. State = BsonReaderState.Type;
  528. }
  529. /// <summary>
  530. /// Reads the start of a BSON document.
  531. /// </summary>
  532. public override void ReadStartDocument()
  533. {
  534. if (Disposed) { ThrowObjectDisposedException(); }
  535. VerifyBsonType("ReadStartDocument", BsonType.Document);
  536. _context = new JsonReaderContext(_context, ContextType.Document);
  537. State = BsonReaderState.Type;
  538. }
  539. /// <summary>
  540. /// Reads a BSON string from the reader.
  541. /// </summary>
  542. /// <returns>A String.</returns>
  543. public override string ReadString()
  544. {
  545. if (Disposed) { ThrowObjectDisposedException(); }
  546. VerifyBsonType("ReadString", BsonType.String);
  547. State = GetNextState();
  548. return _currentValue.AsString;
  549. }
  550. /// <summary>
  551. /// Reads a BSON symbol from the reader.
  552. /// </summary>
  553. /// <returns>A string.</returns>
  554. public override string ReadSymbol()
  555. {
  556. if (Disposed) { ThrowObjectDisposedException(); }
  557. VerifyBsonType("ReadSymbol", BsonType.Symbol);
  558. State = GetNextState();
  559. return _currentValue.AsString;
  560. }
  561. /// <summary>
  562. /// Reads a BSON timestamp from the reader.
  563. /// </summary>
  564. /// <returns>The combined timestamp/increment.</returns>
  565. public override long ReadTimestamp()
  566. {
  567. if (Disposed) { ThrowObjectDisposedException(); }
  568. VerifyBsonType("ReadTimestamp", BsonType.Timestamp);
  569. State = GetNextState();
  570. var timestamp = _currentValue.AsBsonTimestamp;
  571. return timestamp.Value;
  572. }
  573. /// <summary>
  574. /// Reads a BSON undefined from the reader.
  575. /// </summary>
  576. public override void ReadUndefined()
  577. {
  578. if (Disposed) { ThrowObjectDisposedException(); }
  579. VerifyBsonType("ReadUndefined", BsonType.Undefined);
  580. State = GetNextState();
  581. }
  582. /// <summary>
  583. /// Returns the reader to previously bookmarked position and state.
  584. /// </summary>
  585. /// <param name="bookmark">The bookmark.</param>
  586. public override void ReturnToBookmark(BsonReaderBookmark bookmark)
  587. {
  588. if (Disposed) { ThrowObjectDisposedException(); }
  589. var jsonReaderBookmark = (JsonReaderBookmark)bookmark;
  590. State = jsonReaderBookmark.State;
  591. CurrentBsonType = jsonReaderBookmark.CurrentBsonType;
  592. CurrentName = jsonReaderBookmark.CurrentName;
  593. _context = jsonReaderBookmark.CloneContext();
  594. _currentToken = jsonReaderBookmark.CurrentToken;
  595. _currentValue = jsonReaderBookmark.CurrentValue;
  596. _pushedToken = jsonReaderBookmark.PushedToken;
  597. _buffer.Position = jsonReaderBookmark.Position;
  598. }
  599. /// <summary>
  600. /// Skips the name (reader must be positioned on a name).
  601. /// </summary>
  602. public override void SkipName()
  603. {
  604. if (Disposed) { ThrowObjectDisposedException(); }
  605. if (State != BsonReaderState.Name)
  606. {
  607. ThrowInvalidState("SkipName", BsonReaderState.Name);
  608. }
  609. State = BsonReaderState.Value;
  610. }
  611. /// <summary>
  612. /// Skips the value (reader must be positioned on a value).
  613. /// </summary>
  614. public override void SkipValue()
  615. {
  616. if (Disposed) { ThrowObjectDisposedException(); }
  617. if (State != BsonReaderState.Value)
  618. {
  619. ThrowInvalidState("SkipValue", BsonReaderState.Value);
  620. }
  621. switch (CurrentBsonType)
  622. {
  623. case BsonType.Array:
  624. ReadStartArray();
  625. while (ReadBsonType() != BsonType.EndOfDocument)
  626. {
  627. SkipValue();
  628. }
  629. ReadEndArray();
  630. break;
  631. case BsonType.Binary:
  632. ReadBinaryData();
  633. break;
  634. case BsonType.Boolean:
  635. ReadBoolean();
  636. break;
  637. case BsonType.DateTime:
  638. ReadDateTime();
  639. break;
  640. case BsonType.Document:
  641. ReadStartDocument();
  642. while (ReadBsonType() != BsonType.EndOfDocument)
  643. {
  644. SkipName();
  645. SkipValue();
  646. }
  647. ReadEndDocument();
  648. break;
  649. case BsonType.Double:
  650. ReadDouble();
  651. break;
  652. case BsonType.Int32:
  653. ReadInt32();
  654. break;
  655. case BsonType.Int64:
  656. ReadInt64();
  657. break;
  658. case BsonType.JavaScript:
  659. ReadJavaScript();
  660. break;
  661. case BsonType.JavaScriptWithScope:
  662. ReadJavaScriptWithScope();
  663. ReadStartDocument();
  664. while (ReadBsonType() != BsonType.EndOfDocument)
  665. {
  666. SkipName();
  667. SkipValue();
  668. }
  669. ReadEndDocument();
  670. break;
  671. case BsonType.MaxKey:
  672. ReadMaxKey();
  673. break;
  674. case BsonType.MinKey:
  675. ReadMinKey();
  676. break;
  677. case BsonType.Null:
  678. ReadNull();
  679. break;
  680. case BsonType.ObjectId:
  681. ReadObjectId();
  682. break;
  683. case BsonType.RegularExpression:
  684. ReadRegularExpression();
  685. break;
  686. case BsonType.String:
  687. ReadString();
  688. break;
  689. case BsonType.Symbol:
  690. ReadSymbol();
  691. break;
  692. case BsonType.Timestamp:
  693. ReadTimestamp();
  694. break;
  695. case BsonType.Undefined:
  696. ReadUndefined();
  697. break;
  698. default:
  699. throw new BsonInternalException("Invalid BsonType.");
  700. }
  701. }
  702. // protected methods
  703. /// <summary>
  704. /// Disposes of any resources used by the reader.
  705. /// </summary>
  706. /// <param name="disposing">True if called from Dispose.</param>
  707. protected override void Dispose(bool disposing)
  708. {
  709. if (disposing)
  710. {
  711. try
  712. {
  713. Close();
  714. }
  715. catch { } // ignore exceptions
  716. }
  717. base.Dispose(disposing);
  718. }
  719. // private methods
  720. private string FormatInvalidTokenMessage(JsonToken token)
  721. {
  722. return string.Format("Invalid JSON token: '{0}'", token.Lexeme);
  723. }
  724. private string FormatJavaScriptDateTimeString(DateTime dateTime)
  725. {
  726. var utc = BsonUtils.ToUniversalTime(dateTime);
  727. var local = BsonUtils.ToLocalTime(utc);
  728. var offset = local - utc;
  729. var offsetSign = "+";
  730. if (offset < TimeSpan.Zero)
  731. {
  732. offset = -offset;
  733. offsetSign = "-";
  734. }
  735. var timeZone = TimeZone.CurrentTimeZone;
  736. var timeZoneName = local.IsDaylightSavingTime() ? timeZone.DaylightName : timeZone.StandardName;
  737. var dateTimeString = string.Format(
  738. "{0} GMT{1}{2:D2}{3:D2} ({4})",
  739. local.ToString("ddd MMM dd yyyy HH:mm:ss"), offsetSign, offset.Hours, offset.Minutes, timeZoneName);
  740. return dateTimeString;
  741. }
  742. private BsonReaderState GetNextState()
  743. {
  744. switch (_context.ContextType)
  745. {
  746. case ContextType.Array:
  747. case ContextType.Document:
  748. return BsonReaderState.Type;
  749. case ContextType.TopLevel:
  750. return BsonReaderState.Done;
  751. default:
  752. throw new BsonInternalException("Unexpected ContextType.");
  753. }
  754. }
  755. private BsonValue ParseBinDataConstructor()
  756. {
  757. VerifyToken("(");
  758. var subTypeToken = PopToken();
  759. if (subTypeToken.Type != JsonTokenType.Int32)
  760. {
  761. var message = string.Format("JSON reader expected a binary subtype but found '{0}'.", subTypeToken.Lexeme);
  762. throw new Exception(message);
  763. }
  764. VerifyToken(",");
  765. var bytesToken = PopToken();
  766. if (bytesToken.Type != JsonTokenType.String)
  767. {
  768. var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
  769. throw new Exception(message);
  770. }
  771. VerifyToken(")");
  772. var bytes = Convert.FromBase64String(bytesToken.StringValue);
  773. var subType = (BsonBinarySubType)subTypeToken.Int32Value;
  774. GuidRepresentation guidRepresentation;
  775. switch (subType)
  776. {
  777. case BsonBinarySubType.UuidLegacy: guidRepresentation = _jsonReaderSettings.GuidRepresentation; break;
  778. case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
  779. default: guidRepresentation = GuidRepresentation.Unspecified; break;
  780. }
  781. return new BsonBinaryData(bytes, subType, guidRepresentation);
  782. }
  783. private BsonValue ParseBinDataExtendedJson()
  784. {
  785. VerifyToken(":");
  786. var bytesToken = PopToken();
  787. if (bytesToken.Type != JsonTokenType.String)
  788. {
  789. var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
  790. throw new Exception(message);
  791. }
  792. VerifyToken(",");
  793. VerifyString("$type");
  794. VerifyToken(":");
  795. var subTypeToken = PopToken();
  796. if (subTypeToken.Type != JsonTokenType.String)
  797. {
  798. var message = string.Format("JSON reader expected a string but found '{0}'.", subTypeToken.Lexeme);
  799. throw new Exception(message);
  800. }
  801. VerifyToken("}");
  802. var bytes = Convert.FromBase64String(bytesToken.StringValue);
  803. var subType = (BsonBinarySubType)Convert.ToInt32(subTypeToken.StringValue, 16);
  804. GuidRepresentation guidRepresentation;
  805. switch (subType)
  806. {
  807. case BsonBinarySubType.UuidLegacy: guidRepresentation = _jsonReaderSettings.GuidRepresentation; break;
  808. case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
  809. default: guidRepresentation = GuidRepresentation.Unspecified; break;
  810. }
  811. return new BsonBinaryData(bytes, subType, guidRepresentation);
  812. }
  813. private BsonValue ParseHexDataConstructor()
  814. {
  815. VerifyToken("(");
  816. var subTypeToken = PopToken();
  817. if (subTypeToken.Type != JsonTokenType.Int32)
  818. {
  819. var message = string.Format("JSON reader expected a binary subtype but found '{0}'.", subTypeToken.Lexeme);
  820. throw new Exception(message);
  821. }
  822. VerifyToken(",");
  823. var bytesToken = PopToken();
  824. if (bytesToken.Type != JsonTokenType.String)
  825. {
  826. var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
  827. throw new Exception(message);
  828. }
  829. VerifyToken(")");
  830. var bytes = BsonUtils.ParseHexString(bytesToken.StringValue);
  831. var subType = (BsonBinarySubType)subTypeToken.Int32Value;
  832. GuidRepresentation guidRepresentation;
  833. switch (subType)
  834. {
  835. case BsonBinarySubType.UuidLegacy: guidRepresentation = _jsonReaderSettings.GuidRepresentation; break;
  836. case BsonBinarySubType.UuidStandard: guidRepresentation = GuidRepresentation.Standard; break;
  837. default: guidRepresentation = GuidRepresentation.Unspecified; break;
  838. }
  839. return new BsonBinaryData(bytes, subType, guidRepresentation);
  840. }
  841. private BsonType ParseJavaScriptExtendedJson(out BsonValue value)
  842. {
  843. VerifyToken(":");
  844. var codeToken = PopToken();
  845. if (codeToken.Type != JsonTokenType.String)
  846. {
  847. var message = string.Format("JSON reader expected a string but found '{0}'.", codeToken.Lexeme);
  848. throw new Exception(message);
  849. }
  850. var nextToken = PopToken();
  851. switch (nextToken.Type)
  852. {
  853. case JsonTokenType.Comma:
  854. VerifyString("$scope");
  855. VerifyToken(":");
  856. State = BsonReaderState.Value;
  857. value = codeToken.StringValue;
  858. return BsonType.JavaScriptWithScope;
  859. case JsonTokenType.EndObject:
  860. value = codeToken.StringValue;
  861. return BsonType.JavaScript;
  862. default:
  863. var message = string.Format("JSON reader expected ',' or '}' but found '{0}'.", codeToken.Lexeme);
  864. throw new Exception(message);
  865. }
  866. }
  867. private BsonValue ParseISODateTimeConstructor()
  868. {
  869. VerifyToken("(");
  870. var valueToken = PopToken();
  871. if (valueToken.Type != JsonTokenType.String)
  872. {
  873. var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
  874. throw new Exception(message);
  875. }
  876. VerifyToken(")");
  877. var formats = new string[] { "yyyy-MM-ddK", "yyyy-MM-ddTHH:mm:ssK", "yyyy-MM-ddTHH:mm:ss.FFFFFFFK" };
  878. var utcDateTime = DateTime.ParseExact(valueToken.StringValue, formats, null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
  879. return new BsonDateTime(utcDateTime);
  880. }
  881. private BsonValue ParseDateTimeExtendedJson()
  882. {
  883. VerifyToken(":");
  884. var valueToken = PopToken();
  885. long millisecondsSinceEpoch;
  886. if (valueToken.Type == JsonTokenType.Int32 || valueToken.Type == JsonTokenType.Int64)
  887. {
  888. millisecondsSinceEpoch = valueToken.Int64Value;
  889. }
  890. else if (valueToken.Type == JsonTokenType.String)
  891. {
  892. DateTime dateTime;
  893. if (!DateTime.TryParse(valueToken.StringValue, out dateTime))
  894. {
  895. var message = string.Format("Invalid $date string: '{0}'.", valueToken.StringValue);
  896. throw new Exception(message);
  897. }
  898. millisecondsSinceEpoch = BsonUtils.ToMillisecondsSinceEpoch(dateTime);
  899. }
  900. else
  901. {
  902. var message = string.Format("JSON reader expected an integer or an ISO 8601 string for $date but found a '{0}'.", valueToken.Lexeme);
  903. throw new Exception(message);
  904. }
  905. VerifyToken("}");
  906. return new BsonDateTime(millisecondsSinceEpoch);
  907. }
  908. private BsonValue ParseDateTimeConstructor(bool withNew)
  909. {
  910. VerifyToken("(");
  911. // Date when used without "new" behaves differently (JavaScript has some weird parts)
  912. if (!withNew)
  913. {
  914. VerifyToken(")");
  915. var dateTimeString = FormatJavaScriptDateTimeString(DateTime.UtcNow);
  916. return new BsonString(dateTimeString);
  917. }
  918. var token = PopToken();
  919. if (token.Lexeme == ")")
  920. {
  921. return new BsonDateTime(DateTime.UtcNow);
  922. }
  923. else if (token.Type == JsonTokenType.String)
  924. {
  925. VerifyToken(")");
  926. var dateTimeString = token.StringValue;
  927. var dateTime = ParseJavaScriptDateTimeString(dateTimeString);
  928. return new BsonDateTime(dateTime);
  929. }
  930. else if (token.Type == JsonTokenType.Int32 || token.Type == JsonTokenType.Int64)
  931. {
  932. var args = new List<long>();
  933. while (true)
  934. {
  935. args.Add(token.Int64Value);
  936. token = PopToken();
  937. if (token.Lexeme == ")")
  938. {
  939. break;
  940. }
  941. if (token.Lexeme != ",")
  942. {
  943. var message = string.Format("JSON reader expected a ',' or a ')' but found '{0}'.", token.Lexeme);
  944. throw new Exception(message);
  945. }
  946. token = PopToken();
  947. if (token.Type != JsonTokenType.Int32 && token.Type != JsonTokenType.Int64)
  948. {
  949. var message = string.Format("JSON reader expected an integer but found '{0}'.", token.Lexeme);
  950. throw new Exception(message);
  951. }
  952. }
  953. switch (args.Count)
  954. {
  955. case 1:
  956. return new BsonDateTime(args[0]);
  957. case 3:
  958. case 4:
  959. case 5:
  960. case 6:
  961. case 7:
  962. var year = (int)args[0];
  963. var month = (int)args[1] + 1; // JavaScript starts at 0 but .NET starts at 1
  964. var day = (int)args[2];
  965. var hours = (args.Count >= 4) ? (int)args[3] : 0;
  966. var minutes = (args.Count >= 5) ? (int)args[4] : 0;
  967. var seconds = (args.Count >= 6) ? (int)args[5] : 0;
  968. var milliseconds = (args.Count == 7) ? (int)args[6] : 0;
  969. var dateTime = new DateTime(year, month, day, hours, minutes, seconds, milliseconds, DateTimeKind.Utc);
  970. return new BsonDateTime(dateTime);
  971. default:
  972. var message = string.Format("JSON reader expected 1 or 3-7 integers but found {0}.", args.Count);
  973. throw new Exception(message);
  974. }
  975. }
  976. else
  977. {
  978. var message = string.Format("JSON reader expected an integer or a string but found '{0}'.", token.Lexeme);
  979. throw new Exception(message);
  980. }
  981. }
  982. private BsonType ParseExtendedJson()
  983. {
  984. var nameToken = PopToken();
  985. if (nameToken.Type == JsonTokenType.String || nameToken.Type == JsonTokenType.UnquotedString)
  986. {
  987. switch (nameToken.StringValue)
  988. {
  989. case "$binary": _currentValue = ParseBinDataExtendedJson(); return BsonType.Binary;
  990. case "$code": return ParseJavaScriptExtendedJson(out _currentValue);
  991. case "$date": _currentValue = ParseDateTimeExtendedJson(); return BsonType.DateTime;
  992. case "$maxkey": case "$maxKey": _currentValue = ParseMaxKeyExtendedJson(); return BsonType.MaxKey;
  993. case "$minkey": case "$minKey": _currentValue = ParseMinKeyExtendedJson(); return BsonType.MinKey;
  994. case "$numberLong": _currentValue = ParseNumberLongExtendedJson(); return BsonType.Int64;
  995. case "$oid": _currentValue = ParseObjectIdExtendedJson(); return BsonType.ObjectId;
  996. case "$regex": _currentValue = ParseRegularExpressionExtendedJson(); return BsonType.RegularExpression;
  997. case "$symbol": _currentValue = ParseSymbolExtendedJson(); return BsonType.Symbol;
  998. case "$timestamp": _currentValue = ParseTimestampExtendedJson(); return BsonType.Timestamp;
  999. case "$undefined": _currentValue = ParseUndefinedExtendedJson(); return BsonType.Undefined;
  1000. }
  1001. }
  1002. PushToken(nameToken);
  1003. return BsonType.Document;
  1004. }
  1005. private DateTime ParseJavaScriptDateTimeString(string dateTimeString)
  1006. {
  1007. // if DateTime.TryParse succeeds we're done, otherwise assume it's an RFC 822 formatted DateTime string
  1008. DateTime dateTime;
  1009. if (DateTime.TryParse(dateTimeString, out dateTime))
  1010. {
  1011. return dateTime;
  1012. }
  1013. else
  1014. {
  1015. var rfc822DateTimePattern =
  1016. @"^((?<dayOfWeek>(Mon|Tue|Wed|Thu|Fri|Sat|Sun)), )?" +
  1017. @"(?<day>\d{1,2}) +" +
  1018. @"(?<monthName>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " +
  1019. @"(?<year>\d{2}|\d{4}) " +
  1020. @"(?<hour>\d{1,2}):" +
  1021. @"(?<minutes>\d{1,2}):" +
  1022. @"(?<seconds>\d{1,2}(.\d{1,7})?) " +
  1023. @"(?<zone>UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-Z]|([+-]\d{4}))$";
  1024. var match = Regex.Match(dateTimeString, rfc822DateTimePattern);
  1025. if (match.Success)
  1026. {
  1027. var day = int.Parse(match.Groups["day"].Value);
  1028. int month;
  1029. var monthName = match.Groups["monthName"].Value;
  1030. switch (monthName)
  1031. {
  1032. case "Jan": month = 1; break;
  1033. case "Feb": month = 2; break;
  1034. case "Mar": month = 3; break;
  1035. case "Apr": month = 4; break;
  1036. case "May": month = 5; break;
  1037. case "Jun": month = 6; break;
  1038. case "Jul": month = 7; break;
  1039. case "Aug": month = 8; break;
  1040. case "Sep": month = 9; break;
  1041. case "Oct": month = 10; break;
  1042. case "Nov": month = 11; break;
  1043. case "Dec": month = 12; break;
  1044. default:
  1045. var message = string.Format("\"{0}\" is not a valid RFC 822 month name.", monthName);
  1046. throw new Exception(message);
  1047. }
  1048. var yearString = match.Groups["year"].Value;
  1049. int year = int.Parse(yearString);
  1050. if (yearString.Length == 2)
  1051. {
  1052. year += 2000;
  1053. if (year - DateTime.UtcNow.Year >= 19) { year -= 100; }
  1054. }
  1055. var hour = int.Parse(match.Groups["hour"].Value);
  1056. var minutes = int.Parse(match.Groups["minutes"].Value);
  1057. var secondsString = match.Groups["seconds"].Value;
  1058. int seconds;
  1059. double milliseconds;
  1060. if (secondsString.IndexOf('.') != -1)
  1061. {
  1062. var timeSpan = TimeSpan.FromSeconds(double.Parse(secondsString));
  1063. seconds = timeSpan.Seconds;
  1064. milliseconds = timeSpan.TotalMilliseconds - seconds * 1000;
  1065. }
  1066. else
  1067. {
  1068. seconds = int.Parse(secondsString);
  1069. milliseconds = 0;
  1070. }
  1071. dateTime = new DateTime(year, month, day, hour, minutes, seconds, DateTimeKind.Utc).AddMilliseconds(milliseconds);
  1072. // check day of week before converting to UTC
  1073. var dayOfWeekString = match.Groups["dayOfWeek"].Value;
  1074. if (dayOfWeekString != "")
  1075. {
  1076. DayOfWeek dayOfWeek;
  1077. switch (dayOfWeekString)
  1078. {
  1079. case "Mon": dayOfWeek = DayOfWeek.Monday; break;
  1080. case "Tue": dayOfWeek = DayOfWeek.Tuesday; break;
  1081. case "Wed": dayOfWeek = DayOfWeek.Wednesday; break;
  1082. case "Thu": dayOfWeek = DayOfWeek.Thursday; break;
  1083. case "Fri": dayOfWeek = DayOfWeek.Friday; break;
  1084. case "Sat": dayOfWeek = DayOfWeek.Saturday; break;
  1085. case "Sun": dayOfWeek = DayOfWeek.Sunday; break;
  1086. default:
  1087. var message = string.Format("\"{0}\" is not a valid RFC 822 day name.", dayOfWeekString);
  1088. throw new Exception(message);
  1089. }
  1090. if (dateTime.DayOfWeek != dayOfWeek)
  1091. {
  1092. var message = string.Format("\"{0}\" is not the right day of the week for {1}.", dayOfWeekString, dateTime.ToString("o"));
  1093. throw new Exception(message);
  1094. }
  1095. }
  1096. TimeSpan offset;
  1097. var zone = match.Groups["zone"].Value;
  1098. switch (zone)
  1099. {
  1100. case "UT": case "GMT": case "Z": offset = TimeSpan.Zero; break;
  1101. case "EST": offset = TimeSpan.FromHours(-5); break;
  1102. case "EDT": offset = TimeSpan.FromHours(-4); break;
  1103. case "CST": offset = TimeSpan.FromHours(-6); break;
  1104. case "CDT": offset = TimeSpan.FromHours(-5); break;
  1105. case "MST": offset = TimeSpan.FromHours(-7); break;
  1106. case "MDT": offset = TimeSpan.FromHours(-6); break;
  1107. case "PST": offset = TimeSpan.FromHours(-8); break;
  1108. case "PDT": offset = TimeSpan.FromHours(-7); break;
  1109. case "A": offset = TimeSpan.FromHours(-1); break;
  1110. case "B": offset = TimeSpan.FromHours(-2); break;
  1111. case "C": offset = TimeSpan.FromHours(-3); break;
  1112. case "D": offset = TimeSpan.FromHours(-4); break;
  1113. case "E": offset = TimeSpan.FromHours(-5); break;
  1114. case "F": offset = TimeSpan.FromHours(-6); break;
  1115. case "G": offset = TimeSpan.FromHours(-7); break;
  1116. case "H": offset = TimeSpan.FromHours(-8); break;
  1117. case "I": offset = TimeSpan.FromHours(-9); break;
  1118. case "K": offset = TimeSpan.FromHours(-10); break;
  1119. case "L": offset = TimeSpan.FromHours(-11); break;
  1120. case "M": offset = TimeSpan.FromHours(-12); break;
  1121. case "N": offset = TimeSpan.FromHours(1); break;
  1122. case "O": offset = TimeSpan.FromHours(2); break;
  1123. case "P": offset = TimeSpan.FromHours(3); break;
  1124. case "Q": offset = TimeSpan.FromHours(4); break;
  1125. case "R": offset = TimeSpan.FromHours(5); break;
  1126. case "S": offset = TimeSpan.FromHours(6); break;
  1127. case "T": offset = TimeSpan.FromHours(7); break;
  1128. case "U": offset = TimeSpan.FromHours(8); break;
  1129. case "V": offset = TimeSpan.FromHours(9); break;
  1130. case "W": offset = TimeSpan.FromHours(10); break;
  1131. case "X": offset = TimeSpan.FromHours(11); break;
  1132. case "Y": offset = TimeSpan.FromHours(12); break;
  1133. default:
  1134. var offsetSign = zone.Substring(0);
  1135. var offsetHours = zone.Substring(1, 2);
  1136. var offsetMinutes = zone.Substring(3, 2);
  1137. offset = TimeSpan.FromHours(int.Parse(offsetHours)) + TimeSpan.FromMinutes(int.Parse(offsetMinutes));
  1138. if (offsetSign == "-")
  1139. {
  1140. offset = -offset;
  1141. }
  1142. break;
  1143. }
  1144. return dateTime.Add(-offset);
  1145. }
  1146. else
  1147. {
  1148. var message = string.Format("The DateTime string \"{0}\" is not a valid DateTime string for either .NET or JavaScript.", dateTimeString);
  1149. throw new Exception(message);
  1150. }
  1151. }
  1152. }
  1153. private BsonValue ParseMaxKeyExtendedJson()
  1154. {
  1155. VerifyToken(":");
  1156. VerifyToken("1");
  1157. VerifyToken("}");
  1158. return BsonMaxKey.Value;
  1159. }
  1160. private BsonValue ParseMinKeyExtendedJson()
  1161. {
  1162. VerifyToken(":");
  1163. VerifyToken("1");
  1164. VerifyToken("}");
  1165. return BsonMinKey.Value;
  1166. }
  1167. private BsonType ParseNew(out BsonValue value)
  1168. {
  1169. var typeToken = PopToken();
  1170. if (typeToken.Type != JsonTokenType.UnquotedString)
  1171. {
  1172. var message = string.Format("JSON reader expected a type name but found '{0}'.", typeToken.Lexeme);
  1173. throw new Exception(message);
  1174. }
  1175. switch (typeToken.Lexeme)
  1176. {
  1177. case "BinData":
  1178. value = ParseBinDataConstructor();
  1179. return BsonType.Binary;
  1180. case "Date":
  1181. value = ParseDateTimeConstructor(true); // withNew = true
  1182. return BsonType.DateTime;
  1183. case "HexData":
  1184. value = ParseHexDataConstructor();
  1185. return BsonType.Binary;
  1186. case "ISODate":
  1187. value = ParseISODateTimeConstructor();
  1188. return BsonType.DateTime;
  1189. case "NumberInt":
  1190. value = ParseNumberConstructor();
  1191. return BsonType.Int32;
  1192. case "NumberLong":
  1193. value = ParseNumberLongConstructor();
  1194. return BsonType.Int64;
  1195. case "ObjectId":
  1196. value = ParseObjectIdConstructor();
  1197. return BsonType.ObjectId;
  1198. case "RegExp":
  1199. value = ParseRegularExpressionConstructor();
  1200. return BsonType.RegularExpression;
  1201. case "Timestamp":
  1202. value = ParseTimestampConstructor();
  1203. return BsonType.Timestamp;
  1204. case "UUID":
  1205. case "GUID":
  1206. case "CSUUID":
  1207. case "CSGUID":
  1208. case "JUUID":
  1209. case "JGUID":
  1210. case "PYUUID":
  1211. case "PYGUID":
  1212. value = ParseUUIDConstructor(typeToken.Lexeme);
  1213. return BsonType.Binary;
  1214. default:
  1215. var message = string.Format("JSON reader expected a type name but found '{0}'.", typeToken.Lexeme);
  1216. throw new Exception(message);
  1217. }
  1218. }
  1219. private BsonValue ParseNumberConstructor()
  1220. {
  1221. VerifyToken("(");
  1222. var valueToken = PopToken();
  1223. int value;
  1224. if (valueToken.IsNumber)
  1225. {
  1226. value = valueToken.Int32Value;
  1227. }
  1228. else if (valueToken.Type == JsonTokenType.String)
  1229. {
  1230. value = int.Parse(valueToken.StringValue);
  1231. }
  1232. else
  1233. {
  1234. var message = string.Format("JSON reader expected an integer or a string but found '{0}'.", valueToken.Lexeme);
  1235. throw new Exception(message);
  1236. }
  1237. VerifyToken(")");
  1238. return new BsonInt32(value);
  1239. }
  1240. private BsonValue ParseNumberLongConstructor()
  1241. {
  1242. VerifyToken("(");
  1243. var valueToken = PopToken();
  1244. long value;
  1245. if (valueToken.Type == JsonTokenType.Int32 || valueToken.Type == JsonTokenType.Int64)
  1246. {
  1247. value = valueToken.Int64Value;
  1248. }
  1249. else if (valueToken.Type == JsonTokenType.String)
  1250. {
  1251. value = long.Parse(valueToken.StringValue);
  1252. }
  1253. else
  1254. {
  1255. var message = string.Format("JSON reader expected an integer or a string but found '{0}'.", valueToken.Lexeme);
  1256. throw new Exception(message);
  1257. }
  1258. VerifyToken(")");
  1259. return new BsonInt64(value);
  1260. }
  1261. private BsonValue ParseNumberLongExtendedJson()
  1262. {
  1263. VerifyToken(":");
  1264. var valueToken = PopToken();
  1265. if (valueToken.Type != JsonTokenType.String)
  1266. {
  1267. var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
  1268. throw new Exception(message);
  1269. }
  1270. VerifyToken("}");
  1271. return new BsonInt64(long.Parse(valueToken.StringValue));
  1272. }
  1273. private BsonValue ParseObjectIdConstructor()
  1274. {
  1275. VerifyToken("(");
  1276. var valueToken = PopToken();
  1277. if (valueToken.Type != JsonTokenType.String)
  1278. {
  1279. var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
  1280. throw new Exception(message);
  1281. }
  1282. VerifyToken(")");
  1283. return new BsonObjectId(ObjectId.Parse(valueToken.StringValue));
  1284. }
  1285. private BsonValue ParseObjectIdExtendedJson()
  1286. {
  1287. VerifyToken(":");
  1288. var valueToken = PopToken();
  1289. if (valueToken.Type != JsonTokenType.String)
  1290. {
  1291. var message = string.Format("JSON reader expected a string but found '{0}'.", valueToken.Lexeme);
  1292. throw new Exception(message);
  1293. }
  1294. VerifyToken("}");
  1295. return new BsonObjectId(ObjectId.Parse(valueToken.StringValue));
  1296. }
  1297. private BsonValue ParseRegularExpressionConstructor()
  1298. {
  1299. VerifyToken("(");
  1300. var patternToken = PopToken();
  1301. if (patternToken.Type != JsonTokenType.String)
  1302. {
  1303. var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme);
  1304. throw new Exception(message);
  1305. }
  1306. var options = "";
  1307. var commaToken = PopToken();
  1308. if (commaToken.Lexeme == ",")
  1309. {
  1310. var optionsToken = PopToken();
  1311. if (optionsToken.Type != JsonTokenType.String)
  1312. {
  1313. var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme);
  1314. throw new Exception(message);
  1315. }
  1316. options = optionsToken.StringValue;
  1317. }
  1318. else
  1319. {
  1320. PushToken(commaToken);
  1321. }
  1322. VerifyToken(")");
  1323. return new BsonRegularExpression(patternToken.StringValue, options);
  1324. }
  1325. private BsonValue ParseRegularExpressionExtendedJson()
  1326. {
  1327. VerifyToken(":");
  1328. var patternToken = PopToken();
  1329. if (patternToken.Type != JsonTokenType.String)
  1330. {
  1331. var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme);
  1332. throw new Exception(message);
  1333. }
  1334. var options = "";
  1335. var commaToken = PopToken();
  1336. if (commaToken.Lexeme == ",")
  1337. {
  1338. VerifyString("$options");
  1339. VerifyToken(":");
  1340. var optionsToken = PopToken();
  1341. if (optionsToken.Type != JsonTokenType.String)
  1342. {
  1343. var message = string.Format("JSON reader expected a string but found '{0}'.", optionsToken.Lexeme);
  1344. throw new Exception(message);
  1345. }
  1346. options = optionsToken.StringValue;
  1347. }
  1348. else
  1349. {
  1350. PushToken(commaToken);
  1351. }
  1352. VerifyToken("}");
  1353. return new BsonRegularExpression(patternToken.StringValue, options);
  1354. }
  1355. private BsonValue ParseSymbolExtendedJson()
  1356. {
  1357. VerifyToken(":");
  1358. var nameToken = PopToken();
  1359. if (nameToken.Type != JsonTokenType.String)
  1360. {
  1361. var message = string.Format("JSON reader expected a string but found '{0}'.", nameToken.Lexeme);
  1362. throw new Exception(message);
  1363. }
  1364. VerifyToken("}");
  1365. return new BsonString(nameToken.StringValue); // will be converted to a BsonSymbol at a higher level
  1366. }
  1367. private BsonValue ParseTimestampConstructor()
  1368. {
  1369. VerifyToken("(");
  1370. int secondsSinceEpoch;
  1371. var secondsSinceEpochToken = PopToken();
  1372. if (secondsSinceEpochToken.IsNumber)
  1373. {
  1374. secondsSinceEpoch = secondsSinceEpochToken.Int32Value;
  1375. }
  1376. else
  1377. {
  1378. var message = string.Format("JSON reader expected a number but found '{0}'.", secondsSinceEpochToken.Lexeme);
  1379. throw new Exception(message);
  1380. }
  1381. VerifyToken(",");
  1382. int increment;
  1383. var incrementToken = PopToken();
  1384. if (secondsSinceEpochToken.IsNumber)
  1385. {
  1386. increment = incrementToken.Int32Value;
  1387. }
  1388. else
  1389. {
  1390. var message = string.Format("JSON reader expected a number but found '{0}'.", secondsSinceEpochToken.Lexeme);
  1391. throw new Exception(message);
  1392. }
  1393. VerifyToken(")");
  1394. return new BsonTimestamp(secondsSinceEpoch, increment);
  1395. }
  1396. private BsonValue ParseTimestampExtendedJson()
  1397. {
  1398. VerifyToken(":");
  1399. var nextToken = PopToken();
  1400. if (nextToken.Type == JsonTokenType.BeginObject)
  1401. {
  1402. return ParseTimestampExtendedJsonNewRepresentation();
  1403. }
  1404. else
  1405. {
  1406. return ParseTimestampExtendedJsonOldRepresentation(nextToken);
  1407. }
  1408. }
  1409. private BsonValue ParseTimestampExtendedJsonNewRepresentation()
  1410. {
  1411. VerifyString("t");
  1412. VerifyToken(":");
  1413. var secondsSinceEpochToken = PopToken();
  1414. int secondsSinceEpoch;
  1415. if (secondsSinceEpochToken.IsNumber)
  1416. {
  1417. secondsSinceEpoch = secondsSinceEpochToken.Int32Value;
  1418. }
  1419. else
  1420. {
  1421. var message = string.Format("JSON reader expected an integer but found '{0}'.", secondsSinceEpochToken.Lexeme);
  1422. throw new Exception(message);
  1423. }
  1424. VerifyToken(",");
  1425. VerifyString("i");
  1426. VerifyToken(":");
  1427. var incrementToken = PopToken();
  1428. int increment;
  1429. if (incrementToken.IsNumber)
  1430. {
  1431. increment = incrementToken.Int32Value;
  1432. }
  1433. else
  1434. {
  1435. var message = string.Format("JSON reader expected an integer but found '{0}'.", incrementToken.Lexeme);
  1436. throw new Exception(message);
  1437. }
  1438. VerifyToken("}");
  1439. VerifyToken("}");
  1440. return new BsonTimestamp(secondsSinceEpoch, increment);
  1441. }
  1442. private BsonValue ParseTimestampExtendedJsonOldRepresentation(JsonToken valueToken)
  1443. {
  1444. long value;
  1445. if (valueToken.Type == JsonTokenType.Int32 || valueToken.Type == JsonTokenType.Int64)
  1446. {
  1447. value = valueToken.Int64Value;
  1448. }
  1449. else if (valueToken.Type == JsonTokenType.UnquotedString && valueToken.Lexeme == "NumberLong")
  1450. {
  1451. value = ParseNumberLongConstructor().AsInt64;
  1452. }
  1453. else
  1454. {
  1455. var message = string.Format("JSON reader expected an integer but found '{0}'.", valueToken.Lexeme);
  1456. throw new Exception(message);
  1457. }
  1458. VerifyToken("}");
  1459. return new BsonTimestamp(value);
  1460. }
  1461. private BsonValue ParseUndefinedExtendedJson()
  1462. {
  1463. VerifyToken(":");
  1464. VerifyToken("true");
  1465. VerifyToken("}");
  1466. return BsonMaxKey.Value;
  1467. }
  1468. private BsonValue ParseUUIDConstructor(string uuidConstructorName)
  1469. {
  1470. VerifyToken("(");
  1471. var bytesToken = PopToken();
  1472. if (bytesToken.Type != JsonTokenType.String)
  1473. {
  1474. var message = string.Format("JSON reader expected a string but found '{0}'.", bytesToken.Lexeme);
  1475. throw new Exception(message);
  1476. }
  1477. VerifyToken(")");
  1478. var hexString = bytesToken.StringValue.Replace("{", "").Replace("}", "").Replace("-", "");
  1479. var bytes = BsonUtils.ParseHexString(hexString);
  1480. var guid = GuidConverter.FromBytes(bytes, GuidRepresentation.Standard);
  1481. GuidRepresentation guidRepresentation;
  1482. switch (uuidConstructorName)
  1483. {
  1484. case "CSUUID":
  1485. case "CSGUID":
  1486. guidRepresentation = GuidRepresentation.CSharpLegacy;
  1487. break;
  1488. case "JUUID":
  1489. case "JGUID":
  1490. guidRepresentation = GuidRepresentation.JavaLegacy;
  1491. break;
  1492. case "PYUUID":
  1493. case "PYGUID":
  1494. guidRepresentation = GuidRepresentation.PythonLegacy;
  1495. break;
  1496. case "UUID":
  1497. case "GUID":
  1498. guidRepresentation = GuidRepresentation.Standard;
  1499. break;
  1500. default:
  1501. throw new BsonInternalException("Unexpected uuidConstructorName");
  1502. }
  1503. bytes = GuidConverter.ToBytes(guid, guidRepresentation);
  1504. var subType = (guidRepresentation == GuidRepresentation.Standard) ? BsonBinarySubType.UuidStandard : BsonBinarySubType.UuidLegacy;
  1505. return new BsonBinaryData(bytes, subType, guidRepresentation);
  1506. }
  1507. private JsonToken PopToken()
  1508. {
  1509. if (_pushedToken != null)
  1510. {
  1511. var token = _pushedToken;
  1512. _pushedToken = null;
  1513. return token;
  1514. }
  1515. else
  1516. {
  1517. return JsonScanner.GetNextToken(_buffer);
  1518. }
  1519. }
  1520. private void PushToken(JsonToken token)
  1521. {
  1522. if (_pushedToken == null)
  1523. {
  1524. _pushedToken = token;
  1525. }
  1526. else
  1527. {
  1528. throw new BsonInternalException("There is already a pending token.");
  1529. }
  1530. }
  1531. private void VerifyString(string expectedString)
  1532. {
  1533. var token = PopToken();
  1534. if ((token.Type != JsonTokenType.String && token.Type != JsonTokenType.UnquotedString) || token.StringValue != expectedString)
  1535. {
  1536. var message = string.Format("JSON reader expected '{0}' but found '{1}'.", expectedString, token.StringValue);
  1537. throw new Exception(message);
  1538. }
  1539. }
  1540. private void VerifyToken(string expectedLexeme)
  1541. {
  1542. var token = PopToken();
  1543. if (token.Lexeme != expectedLexeme)
  1544. {
  1545. var message = string.Format("JSON reader expected '{0}' but found '{1}'.", expectedLexeme, token.Lexeme);
  1546. throw new Exception(message);
  1547. }
  1548. }
  1549. }
  1550. }