JsonWriter.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. /* Copyright 2010-present 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.Globalization;
  17. using System.IO;
  18. using System.Linq;
  19. using System.Text;
  20. using System.Text.RegularExpressions;
  21. namespace MongoDB.Bson.IO
  22. {
  23. /// <summary>
  24. /// Represents a BSON writer to a TextWriter (in JSON format).
  25. /// </summary>
  26. public class JsonWriter : BsonWriter
  27. {
  28. // private fields
  29. private TextWriter _textWriter;
  30. private JsonWriterContext _context;
  31. // constructors
  32. /// <summary>
  33. /// Initializes a new instance of the JsonWriter class.
  34. /// </summary>
  35. /// <param name="writer">A TextWriter.</param>
  36. public JsonWriter(TextWriter writer)
  37. : this(writer, JsonWriterSettings.Defaults)
  38. {
  39. }
  40. /// <summary>
  41. /// Initializes a new instance of the JsonWriter class.
  42. /// </summary>
  43. /// <param name="writer">A TextWriter.</param>
  44. /// <param name="settings">Optional JsonWriter settings.</param>
  45. public JsonWriter(TextWriter writer, JsonWriterSettings settings)
  46. : base(settings)
  47. {
  48. if (writer == null)
  49. {
  50. throw new ArgumentNullException("writer");
  51. }
  52. _textWriter = writer;
  53. _context = new JsonWriterContext(null, ContextType.TopLevel, "");
  54. State = BsonWriterState.Initial;
  55. }
  56. // public properties
  57. /// <summary>
  58. /// Gets the base TextWriter.
  59. /// </summary>
  60. /// <value>
  61. /// The base TextWriter.
  62. /// </value>
  63. public TextWriter BaseTextWriter
  64. {
  65. get { return _textWriter; }
  66. }
  67. /// <inheritdoc />
  68. public override long Position => 0L;
  69. /// <summary>
  70. /// Gets the settings of the writer.
  71. /// </summary>
  72. public new JsonWriterSettings Settings
  73. {
  74. get { return (JsonWriterSettings)base.Settings; }
  75. }
  76. // public methods
  77. /// <summary>
  78. /// Closes the writer.
  79. /// </summary>
  80. public override void Close()
  81. {
  82. // Close can be called on Disposed objects
  83. if (State != BsonWriterState.Closed)
  84. {
  85. Flush();
  86. _context = null;
  87. State = BsonWriterState.Closed;
  88. }
  89. }
  90. /// <summary>
  91. /// Flushes any pending data to the output destination.
  92. /// </summary>
  93. public override void Flush()
  94. {
  95. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  96. _textWriter.Flush();
  97. }
  98. /// <summary>
  99. /// Writes BSON binary data to the writer.
  100. /// </summary>
  101. /// <param name="binaryData">The binary data.</param>
  102. public override void WriteBinaryData(BsonBinaryData binaryData)
  103. {
  104. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  105. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  106. {
  107. ThrowInvalidState("WriteBinaryData", BsonWriterState.Value, BsonWriterState.Initial);
  108. }
  109. var subType = binaryData.SubType;
  110. var bytes = binaryData.Bytes;
  111. #pragma warning disable 618
  112. GuidRepresentation guidRepresentation;
  113. if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
  114. {
  115. guidRepresentation = binaryData.GuidRepresentation;
  116. }
  117. else
  118. {
  119. guidRepresentation = subType == BsonBinarySubType.UuidStandard ? GuidRepresentation.Standard : GuidRepresentation.Unspecified;
  120. }
  121. #pragma warning restore 618
  122. WriteNameHelper(Name);
  123. switch (Settings.OutputMode)
  124. {
  125. #pragma warning disable 618
  126. case JsonOutputMode.Strict:
  127. #pragma warning restore 618
  128. _textWriter.Write("{{ \"$binary\" : \"{0}\", \"$type\" : \"{1}\" }}", Convert.ToBase64String(bytes), ((int)subType).ToString("x2"));
  129. break;
  130. case JsonOutputMode.CanonicalExtendedJson:
  131. case JsonOutputMode.RelaxedExtendedJson:
  132. _textWriter.Write("{{ \"$binary\" : {{ \"base64\" : \"{0}\", \"subType\" : \"{1}\" }} }}", Convert.ToBase64String(bytes), ((int)subType).ToString("x2"));
  133. break;
  134. case JsonOutputMode.Shell:
  135. default:
  136. switch (subType)
  137. {
  138. case BsonBinarySubType.UuidLegacy:
  139. case BsonBinarySubType.UuidStandard:
  140. _textWriter.Write(GuidToString(subType, bytes, guidRepresentation));
  141. break;
  142. default:
  143. _textWriter.Write("new BinData({0}, \"{1}\")", (int)subType, Convert.ToBase64String(bytes));
  144. break;
  145. }
  146. break;
  147. }
  148. State = GetNextState();
  149. }
  150. /// <summary>
  151. /// Writes a BSON Boolean to the writer.
  152. /// </summary>
  153. /// <param name="value">The Boolean value.</param>
  154. public override void WriteBoolean(bool value)
  155. {
  156. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  157. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  158. {
  159. ThrowInvalidState("WriteBoolean", BsonWriterState.Value, BsonWriterState.Initial);
  160. }
  161. WriteNameHelper(Name);
  162. _textWriter.Write(value ? "true" : "false");
  163. State = GetNextState();
  164. }
  165. /// <summary>
  166. /// Writes BSON binary data to the writer.
  167. /// </summary>
  168. /// <param name="bytes">The bytes.</param>
  169. public override void WriteBytes(byte[] bytes)
  170. {
  171. WriteBinaryData(new BsonBinaryData(bytes, BsonBinarySubType.Binary));
  172. }
  173. /// <summary>
  174. /// Writes a BSON DateTime to the writer.
  175. /// </summary>
  176. /// <param name="value">The number of milliseconds since the Unix epoch.</param>
  177. public override void WriteDateTime(long value)
  178. {
  179. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  180. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  181. {
  182. ThrowInvalidState("WriteDateTime", BsonWriterState.Value, BsonWriterState.Initial);
  183. }
  184. WriteNameHelper(Name);
  185. switch (Settings.OutputMode)
  186. {
  187. #pragma warning disable 618
  188. case JsonOutputMode.Strict:
  189. #pragma warning restore 618
  190. _textWriter.Write("{{ \"$date\" : {0} }}", value);
  191. break;
  192. case JsonOutputMode.RelaxedExtendedJson:
  193. if (value >= 0 && value <= BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch)
  194. {
  195. var utcDateTime = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(value);
  196. _textWriter.Write("{{ \"$date\" : \"{0}\" }}", utcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ"));
  197. }
  198. else
  199. {
  200. _textWriter.Write("{{ \"$date\" : {{ \"$numberLong\" : \"{0}\" }} }}", value);
  201. }
  202. break;
  203. case JsonOutputMode.CanonicalExtendedJson:
  204. _textWriter.Write("{{ \"$date\" : {{ \"$numberLong\" : \"{0}\" }} }}", value);
  205. break;
  206. case JsonOutputMode.Shell:
  207. default:
  208. // use ISODate for values that fall within .NET's DateTime range, and "new Date" for all others
  209. if (value >= BsonConstants.DateTimeMinValueMillisecondsSinceEpoch &&
  210. value <= BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch)
  211. {
  212. var utcDateTime = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(value);
  213. _textWriter.Write("ISODate(\"{0}\")", utcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ"));
  214. }
  215. else
  216. {
  217. _textWriter.Write("new Date({0})", value);
  218. }
  219. break;
  220. }
  221. State = GetNextState();
  222. }
  223. /// <inheritdoc />
  224. public override void WriteDecimal128(Decimal128 value)
  225. {
  226. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  227. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  228. {
  229. ThrowInvalidState(nameof(WriteDecimal128), BsonWriterState.Value, BsonWriterState.Initial);
  230. }
  231. WriteNameHelper(Name);
  232. switch (Settings.OutputMode)
  233. {
  234. case JsonOutputMode.Shell:
  235. _textWriter.Write("NumberDecimal(\"{0}\")", value.ToString());
  236. break;
  237. #pragma warning disable 618
  238. case JsonOutputMode.Strict:
  239. #pragma warning restore 618
  240. case JsonOutputMode.CanonicalExtendedJson:
  241. case JsonOutputMode.RelaxedExtendedJson:
  242. default:
  243. _textWriter.Write("{{ \"$numberDecimal\" : \"{0}\" }}", value.ToString());
  244. break;
  245. }
  246. State = GetNextState();
  247. }
  248. /// <summary>
  249. /// Writes a BSON Double to the writer.
  250. /// </summary>
  251. /// <param name="value">The Double value.</param>
  252. public override void WriteDouble(double value)
  253. {
  254. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  255. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  256. {
  257. ThrowInvalidState("WriteDouble", BsonWriterState.Value, BsonWriterState.Initial);
  258. }
  259. // if string representation looks like an integer add ".0" so that it looks like a double
  260. var stringRepresentation = JsonConvert.ToString(value);
  261. if (Regex.IsMatch(stringRepresentation, @"^[+-]?\d+$"))
  262. {
  263. stringRepresentation += ".0";
  264. }
  265. WriteNameHelper(Name);
  266. switch (Settings.OutputMode)
  267. {
  268. case JsonOutputMode.CanonicalExtendedJson:
  269. _textWriter.Write("{{ \"$numberDouble\" : \"{0}\" }}", stringRepresentation);
  270. break;
  271. case JsonOutputMode.RelaxedExtendedJson:
  272. if (double.IsNaN(value) || double.IsInfinity(value))
  273. {
  274. _textWriter.Write("{{ \"$numberDouble\" : \"{0}\" }}", stringRepresentation);
  275. }
  276. else
  277. {
  278. _textWriter.Write(stringRepresentation);
  279. }
  280. break;
  281. #pragma warning disable 618
  282. case JsonOutputMode.Strict:
  283. #pragma warning restore 618
  284. case JsonOutputMode.Shell:
  285. default:
  286. _textWriter.Write(stringRepresentation);
  287. break;
  288. }
  289. State = GetNextState();
  290. }
  291. /// <summary>
  292. /// Writes the end of a BSON array to the writer.
  293. /// </summary>
  294. public override void WriteEndArray()
  295. {
  296. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  297. if (State != BsonWriterState.Value)
  298. {
  299. ThrowInvalidState("WriteEndArray", BsonWriterState.Value);
  300. }
  301. base.WriteEndArray();
  302. _textWriter.Write("]");
  303. _context = _context.ParentContext;
  304. State = GetNextState();
  305. }
  306. /// <summary>
  307. /// Writes the end of a BSON document to the writer.
  308. /// </summary>
  309. public override void WriteEndDocument()
  310. {
  311. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  312. if (State != BsonWriterState.Name)
  313. {
  314. ThrowInvalidState("WriteEndDocument", BsonWriterState.Name);
  315. }
  316. base.WriteEndDocument();
  317. if (Settings.Indent && _context.HasElements)
  318. {
  319. _textWriter.Write(Settings.NewLineChars);
  320. if (_context.ParentContext != null)
  321. {
  322. _textWriter.Write(_context.ParentContext.Indentation);
  323. }
  324. _textWriter.Write("}");
  325. }
  326. else
  327. {
  328. _textWriter.Write(" }");
  329. }
  330. if (_context.ContextType == ContextType.ScopeDocument)
  331. {
  332. _context = _context.ParentContext;
  333. WriteEndDocument();
  334. }
  335. else
  336. {
  337. _context = _context.ParentContext;
  338. }
  339. if (_context == null)
  340. {
  341. State = BsonWriterState.Done;
  342. }
  343. else
  344. {
  345. State = GetNextState();
  346. }
  347. }
  348. /// <summary>
  349. /// Writes a BSON Int32 to the writer.
  350. /// </summary>
  351. /// <param name="value">The Int32 value.</param>
  352. public override void WriteInt32(int value)
  353. {
  354. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  355. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  356. {
  357. ThrowInvalidState("WriteInt32", BsonWriterState.Value, BsonWriterState.Initial);
  358. }
  359. WriteNameHelper(Name);
  360. switch (Settings.OutputMode)
  361. {
  362. case JsonOutputMode.CanonicalExtendedJson:
  363. _textWriter.Write("{{ \"$numberInt\" : \"{0}\" }}", value);
  364. break;
  365. #pragma warning disable 618
  366. case JsonOutputMode.Strict:
  367. #pragma warning restore 618
  368. case JsonOutputMode.RelaxedExtendedJson:
  369. case JsonOutputMode.Shell:
  370. default:
  371. _textWriter.Write(value);
  372. break;
  373. }
  374. State = GetNextState();
  375. }
  376. /// <summary>
  377. /// Writes a BSON Int64 to the writer.
  378. /// </summary>
  379. /// <param name="value">The Int64 value.</param>
  380. public override void WriteInt64(long value)
  381. {
  382. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  383. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  384. {
  385. ThrowInvalidState("WriteInt64", BsonWriterState.Value, BsonWriterState.Initial);
  386. }
  387. WriteNameHelper(Name);
  388. switch (Settings.OutputMode)
  389. {
  390. #pragma warning disable 618
  391. case JsonOutputMode.Strict:
  392. #pragma warning restore 618
  393. case JsonOutputMode.RelaxedExtendedJson:
  394. _textWriter.Write(value);
  395. break;
  396. case JsonOutputMode.CanonicalExtendedJson:
  397. _textWriter.Write("{{ \"$numberLong\" : \"{0}\" }}", value);
  398. break;
  399. case JsonOutputMode.Shell:
  400. default:
  401. if (value >= int.MinValue && value <= int.MaxValue)
  402. {
  403. _textWriter.Write("NumberLong({0})", value);
  404. }
  405. else
  406. {
  407. _textWriter.Write("NumberLong(\"{0}\")", value);
  408. }
  409. break;
  410. }
  411. State = GetNextState();
  412. }
  413. /// <summary>
  414. /// Writes a BSON JavaScript to the writer.
  415. /// </summary>
  416. /// <param name="code">The JavaScript code.</param>
  417. public override void WriteJavaScript(string code)
  418. {
  419. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  420. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  421. {
  422. ThrowInvalidState("WriteJavaScript", BsonWriterState.Value, BsonWriterState.Initial);
  423. }
  424. WriteNameHelper(Name);
  425. _textWriter.Write("{{ \"$code\" : \"{0}\" }}", EscapedString(code));
  426. State = GetNextState();
  427. }
  428. /// <summary>
  429. /// Writes a BSON JavaScript to the writer (call WriteStartDocument to start writing the scope).
  430. /// </summary>
  431. /// <param name="code">The JavaScript code.</param>
  432. public override void WriteJavaScriptWithScope(string code)
  433. {
  434. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  435. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  436. {
  437. ThrowInvalidState("WriteJavaScriptWithScope", BsonWriterState.Value, BsonWriterState.Initial);
  438. }
  439. WriteStartDocument();
  440. WriteName("$code");
  441. WriteString(code);
  442. WriteName("$scope");
  443. State = BsonWriterState.ScopeDocument;
  444. }
  445. /// <summary>
  446. /// Writes a BSON MaxKey to the writer.
  447. /// </summary>
  448. public override void WriteMaxKey()
  449. {
  450. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  451. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  452. {
  453. ThrowInvalidState("WriteMaxKey", BsonWriterState.Value, BsonWriterState.Initial);
  454. }
  455. WriteNameHelper(Name);
  456. switch (Settings.OutputMode)
  457. {
  458. #pragma warning disable 618
  459. case JsonOutputMode.Strict:
  460. #pragma warning restore 618
  461. case JsonOutputMode.CanonicalExtendedJson:
  462. case JsonOutputMode.RelaxedExtendedJson:
  463. _textWriter.Write("{ \"$maxKey\" : 1 }");
  464. break;
  465. case JsonOutputMode.Shell:
  466. default:
  467. _textWriter.Write("MaxKey");
  468. break;
  469. }
  470. State = GetNextState();
  471. }
  472. /// <summary>
  473. /// Writes a BSON MinKey to the writer.
  474. /// </summary>
  475. public override void WriteMinKey()
  476. {
  477. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  478. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  479. {
  480. ThrowInvalidState("WriteMinKey", BsonWriterState.Value, BsonWriterState.Initial);
  481. }
  482. WriteNameHelper(Name);
  483. switch (Settings.OutputMode)
  484. {
  485. #pragma warning disable 618
  486. case JsonOutputMode.Strict:
  487. #pragma warning restore 618
  488. case JsonOutputMode.CanonicalExtendedJson:
  489. case JsonOutputMode.RelaxedExtendedJson:
  490. _textWriter.Write("{ \"$minKey\" : 1 }");
  491. break;
  492. case JsonOutputMode.Shell:
  493. default:
  494. _textWriter.Write("MinKey");
  495. break;
  496. }
  497. State = GetNextState();
  498. }
  499. /// <summary>
  500. /// Writes a BSON null to the writer.
  501. /// </summary>
  502. public override void WriteNull()
  503. {
  504. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  505. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  506. {
  507. ThrowInvalidState("WriteNull", BsonWriterState.Value, BsonWriterState.Initial);
  508. }
  509. WriteNameHelper(Name);
  510. _textWriter.Write("null");
  511. State = GetNextState();
  512. }
  513. /// <summary>
  514. /// Writes a BSON ObjectId to the writer.
  515. /// </summary>
  516. /// <param name="objectId">The ObjectId.</param>
  517. public override void WriteObjectId(ObjectId objectId)
  518. {
  519. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  520. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  521. {
  522. ThrowInvalidState("WriteObjectId", BsonWriterState.Value, BsonWriterState.Initial);
  523. }
  524. WriteNameHelper(Name);
  525. switch (Settings.OutputMode)
  526. {
  527. #pragma warning disable 618
  528. case JsonOutputMode.Strict:
  529. #pragma warning restore 618
  530. case JsonOutputMode.CanonicalExtendedJson:
  531. case JsonOutputMode.RelaxedExtendedJson:
  532. _textWriter.Write("{{ \"$oid\" : \"{0}\" }}", objectId.ToString());
  533. break;
  534. case JsonOutputMode.Shell:
  535. default:
  536. _textWriter.Write("ObjectId(\"{0}\")", objectId.ToString());
  537. break;
  538. }
  539. State = GetNextState();
  540. }
  541. /// <summary>
  542. /// Writes a BSON regular expression to the writer.
  543. /// </summary>
  544. /// <param name="regex">A BsonRegularExpression.</param>
  545. public override void WriteRegularExpression(BsonRegularExpression regex)
  546. {
  547. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  548. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  549. {
  550. ThrowInvalidState("WriteRegularExpression", BsonWriterState.Value, BsonWriterState.Initial);
  551. }
  552. var pattern = regex.Pattern;
  553. var options = regex.Options;
  554. WriteNameHelper(Name);
  555. switch (Settings.OutputMode)
  556. {
  557. #pragma warning disable 618
  558. case JsonOutputMode.Strict:
  559. #pragma warning restore 618
  560. _textWriter.Write("{{ \"$regex\" : \"{0}\", \"$options\" : \"{1}\" }}", EscapedString(pattern), EscapedString(options));
  561. break;
  562. case JsonOutputMode.CanonicalExtendedJson:
  563. case JsonOutputMode.RelaxedExtendedJson:
  564. _textWriter.Write("{{ \"$regularExpression\" : {{ \"pattern\" : \"{0}\", \"options\" : \"{1}\" }} }}", EscapedString(pattern), EscapedString(options));
  565. break;
  566. case JsonOutputMode.Shell:
  567. default:
  568. var escapedPattern = (pattern == "") ? "(?:)" : pattern.Replace("/", @"\/");
  569. _textWriter.Write("/{0}/{1}", escapedPattern, options);
  570. break;
  571. }
  572. State = GetNextState();
  573. }
  574. /// <summary>
  575. /// Writes the start of a BSON array to the writer.
  576. /// </summary>
  577. public override void WriteStartArray()
  578. {
  579. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  580. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  581. {
  582. ThrowInvalidState("WriteStartArray", BsonWriterState.Value, BsonWriterState.Initial);
  583. }
  584. base.WriteStartArray();
  585. WriteNameHelper(Name);
  586. _textWriter.Write("[");
  587. _context = new JsonWriterContext(_context, ContextType.Array, Settings.IndentChars);
  588. State = BsonWriterState.Value;
  589. }
  590. /// <summary>
  591. /// Writes the start of a BSON document to the writer.
  592. /// </summary>
  593. public override void WriteStartDocument()
  594. {
  595. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  596. if (State != BsonWriterState.Value && State != BsonWriterState.Initial && State != BsonWriterState.ScopeDocument)
  597. {
  598. ThrowInvalidState("WriteStartDocument", BsonWriterState.Value, BsonWriterState.Initial, BsonWriterState.ScopeDocument);
  599. }
  600. base.WriteStartDocument();
  601. if (State == BsonWriterState.Value || State == BsonWriterState.ScopeDocument)
  602. {
  603. WriteNameHelper(Name);
  604. }
  605. _textWriter.Write("{");
  606. var contextType = (State == BsonWriterState.ScopeDocument) ? ContextType.ScopeDocument : ContextType.Document;
  607. _context = new JsonWriterContext(_context, contextType, Settings.IndentChars);
  608. State = BsonWriterState.Name;
  609. }
  610. /// <summary>
  611. /// Writes a BSON String to the writer.
  612. /// </summary>
  613. /// <param name="value">The String value.</param>
  614. public override void WriteString(string value)
  615. {
  616. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  617. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  618. {
  619. ThrowInvalidState("WriteString", BsonWriterState.Value, BsonWriterState.Initial);
  620. }
  621. WriteNameHelper(Name);
  622. WriteQuotedString(value);
  623. State = GetNextState();
  624. }
  625. /// <summary>
  626. /// Writes a BSON Symbol to the writer.
  627. /// </summary>
  628. /// <param name="value">The symbol.</param>
  629. public override void WriteSymbol(string value)
  630. {
  631. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  632. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  633. {
  634. ThrowInvalidState("WriteSymbol", BsonWriterState.Value, BsonWriterState.Initial);
  635. }
  636. WriteNameHelper(Name);
  637. _textWriter.Write("{{ \"$symbol\" : \"{0}\" }}", EscapedString(value));
  638. State = GetNextState();
  639. }
  640. /// <summary>
  641. /// Writes a BSON timestamp to the writer.
  642. /// </summary>
  643. /// <param name="value">The combined timestamp/increment value.</param>
  644. public override void WriteTimestamp(long value)
  645. {
  646. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  647. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  648. {
  649. ThrowInvalidState("WriteTimestamp", BsonWriterState.Value, BsonWriterState.Initial);
  650. }
  651. var secondsSinceEpoch = (uint)((value >> 32) & 0xffffffff);
  652. var increment = (uint)(value & 0xffffffff);
  653. WriteNameHelper(Name);
  654. switch (Settings.OutputMode)
  655. {
  656. #pragma warning disable 618
  657. case JsonOutputMode.Strict:
  658. #pragma warning restore 618
  659. case JsonOutputMode.CanonicalExtendedJson:
  660. case JsonOutputMode.RelaxedExtendedJson:
  661. _textWriter.Write("{{ \"$timestamp\" : {{ \"t\" : {0}, \"i\" : {1} }} }}", secondsSinceEpoch, increment);
  662. break;
  663. case JsonOutputMode.Shell:
  664. default:
  665. _textWriter.Write("Timestamp({0}, {1})", secondsSinceEpoch, increment);
  666. break;
  667. }
  668. State = GetNextState();
  669. }
  670. /// <summary>
  671. /// Writes a BSON undefined to the writer.
  672. /// </summary>
  673. public override void WriteUndefined()
  674. {
  675. if (Disposed) { throw new ObjectDisposedException("JsonWriter"); }
  676. if (State != BsonWriterState.Value && State != BsonWriterState.Initial)
  677. {
  678. ThrowInvalidState("WriteUndefined", BsonWriterState.Value, BsonWriterState.Initial);
  679. }
  680. WriteNameHelper(Name);
  681. switch (Settings.OutputMode)
  682. {
  683. #pragma warning disable 618
  684. case JsonOutputMode.Strict:
  685. #pragma warning restore 618
  686. case JsonOutputMode.CanonicalExtendedJson:
  687. case JsonOutputMode.RelaxedExtendedJson:
  688. _textWriter.Write("{ \"$undefined\" : true }");
  689. break;
  690. case JsonOutputMode.Shell:
  691. default:
  692. _textWriter.Write("undefined");
  693. break;
  694. }
  695. State = GetNextState();
  696. }
  697. // protected methods
  698. /// <summary>
  699. /// Disposes of any resources used by the writer.
  700. /// </summary>
  701. /// <param name="disposing">True if called from Dispose.</param>
  702. protected override void Dispose(bool disposing)
  703. {
  704. if (disposing)
  705. {
  706. try
  707. {
  708. Close();
  709. }
  710. catch { } // ignore exceptions
  711. }
  712. base.Dispose(disposing);
  713. }
  714. // private methods
  715. private string EscapedString(string value)
  716. {
  717. if (value.All(c => !NeedsEscaping(c)))
  718. {
  719. return value;
  720. }
  721. var sb = new StringBuilder(value.Length);
  722. foreach (char c in value)
  723. {
  724. switch (c)
  725. {
  726. case '"': sb.Append("\\\""); break;
  727. case '\\': sb.Append("\\\\"); break;
  728. case '\b': sb.Append("\\b"); break;
  729. case '\f': sb.Append("\\f"); break;
  730. case '\n': sb.Append("\\n"); break;
  731. case '\r': sb.Append("\\r"); break;
  732. case '\t': sb.Append("\\t"); break;
  733. default:
  734. switch (CharUnicodeInfo.GetUnicodeCategory(c))
  735. {
  736. case UnicodeCategory.UppercaseLetter:
  737. case UnicodeCategory.LowercaseLetter:
  738. case UnicodeCategory.TitlecaseLetter:
  739. case UnicodeCategory.OtherLetter:
  740. case UnicodeCategory.DecimalDigitNumber:
  741. case UnicodeCategory.LetterNumber:
  742. case UnicodeCategory.OtherNumber:
  743. case UnicodeCategory.SpaceSeparator:
  744. case UnicodeCategory.ConnectorPunctuation:
  745. case UnicodeCategory.DashPunctuation:
  746. case UnicodeCategory.OpenPunctuation:
  747. case UnicodeCategory.ClosePunctuation:
  748. case UnicodeCategory.InitialQuotePunctuation:
  749. case UnicodeCategory.FinalQuotePunctuation:
  750. case UnicodeCategory.OtherPunctuation:
  751. case UnicodeCategory.MathSymbol:
  752. case UnicodeCategory.CurrencySymbol:
  753. case UnicodeCategory.ModifierSymbol:
  754. case UnicodeCategory.OtherSymbol:
  755. sb.Append(c);
  756. break;
  757. default:
  758. sb.AppendFormat("\\u{0:x4}", (int)c);
  759. break;
  760. }
  761. break;
  762. }
  763. }
  764. return sb.ToString();
  765. }
  766. private BsonWriterState GetNextState()
  767. {
  768. if (_context.ContextType == ContextType.Array || _context.ContextType == ContextType.TopLevel)
  769. {
  770. return BsonWriterState.Value;
  771. }
  772. else
  773. {
  774. return BsonWriterState.Name;
  775. }
  776. }
  777. private string GuidToString(BsonBinarySubType subType, byte[] bytes, GuidRepresentation guidRepresentation)
  778. {
  779. if (bytes.Length != 16)
  780. {
  781. var message = string.Format("Length of binary subtype {0} must be 16, not {1}.", subType, bytes.Length);
  782. throw new ArgumentException(message);
  783. }
  784. if (subType == BsonBinarySubType.UuidLegacy)
  785. {
  786. if (guidRepresentation == GuidRepresentation.Standard)
  787. {
  788. throw new ArgumentException("GuidRepresentation for binary subtype UuidLegacy must not be Standard.");
  789. }
  790. }
  791. if (subType == BsonBinarySubType.UuidStandard)
  792. {
  793. if (guidRepresentation == GuidRepresentation.Unspecified)
  794. {
  795. guidRepresentation = GuidRepresentation.Standard;
  796. }
  797. if (guidRepresentation != GuidRepresentation.Standard)
  798. {
  799. var message = string.Format("GuidRepresentation for binary subtype UuidStandard must be Standard, not {0}.", guidRepresentation);
  800. throw new ArgumentException(message);
  801. }
  802. }
  803. if (guidRepresentation == GuidRepresentation.Unspecified)
  804. {
  805. var s = BsonUtils.ToHexString(bytes);
  806. var parts = new string[]
  807. {
  808. s.Substring(0, 8),
  809. s.Substring(8, 4),
  810. s.Substring(12, 4),
  811. s.Substring(16, 4),
  812. s.Substring(20, 12)
  813. };
  814. return string.Format("HexData({0}, \"{1}\")", (int)subType, string.Join("-", parts));
  815. }
  816. else
  817. {
  818. string uuidConstructorName;
  819. switch (guidRepresentation)
  820. {
  821. case GuidRepresentation.CSharpLegacy: uuidConstructorName = "CSUUID"; break;
  822. case GuidRepresentation.JavaLegacy: uuidConstructorName = "JUUID"; break;
  823. case GuidRepresentation.PythonLegacy: uuidConstructorName = "PYUUID"; break;
  824. case GuidRepresentation.Standard: uuidConstructorName = "UUID"; break;
  825. default: throw new BsonInternalException("Unexpected GuidRepresentation");
  826. }
  827. var guid = GuidConverter.FromBytes(bytes, guidRepresentation);
  828. return string.Format("{0}(\"{1}\")", uuidConstructorName, guid.ToString());
  829. }
  830. }
  831. private bool NeedsEscaping(char c)
  832. {
  833. switch (c)
  834. {
  835. case '"':
  836. case '\\':
  837. case '\b':
  838. case '\f':
  839. case '\n':
  840. case '\r':
  841. case '\t':
  842. return true;
  843. default:
  844. switch (CharUnicodeInfo.GetUnicodeCategory(c))
  845. {
  846. case UnicodeCategory.UppercaseLetter:
  847. case UnicodeCategory.LowercaseLetter:
  848. case UnicodeCategory.TitlecaseLetter:
  849. case UnicodeCategory.OtherLetter:
  850. case UnicodeCategory.DecimalDigitNumber:
  851. case UnicodeCategory.LetterNumber:
  852. case UnicodeCategory.OtherNumber:
  853. case UnicodeCategory.SpaceSeparator:
  854. case UnicodeCategory.ConnectorPunctuation:
  855. case UnicodeCategory.DashPunctuation:
  856. case UnicodeCategory.OpenPunctuation:
  857. case UnicodeCategory.ClosePunctuation:
  858. case UnicodeCategory.InitialQuotePunctuation:
  859. case UnicodeCategory.FinalQuotePunctuation:
  860. case UnicodeCategory.OtherPunctuation:
  861. case UnicodeCategory.MathSymbol:
  862. case UnicodeCategory.CurrencySymbol:
  863. case UnicodeCategory.ModifierSymbol:
  864. case UnicodeCategory.OtherSymbol:
  865. return false;
  866. default:
  867. return true;
  868. }
  869. }
  870. }
  871. private void WriteNameHelper(string name)
  872. {
  873. switch (_context.ContextType)
  874. {
  875. case ContextType.Array:
  876. // don't write Array element names in Json
  877. if (_context.HasElements)
  878. {
  879. _textWriter.Write(", ");
  880. }
  881. break;
  882. case ContextType.Document:
  883. case ContextType.ScopeDocument:
  884. if (_context.HasElements)
  885. {
  886. _textWriter.Write(",");
  887. }
  888. if (Settings.Indent)
  889. {
  890. _textWriter.Write(Settings.NewLineChars);
  891. _textWriter.Write(_context.Indentation);
  892. }
  893. else
  894. {
  895. _textWriter.Write(" ");
  896. }
  897. WriteQuotedString(name);
  898. _textWriter.Write(" : ");
  899. break;
  900. case ContextType.TopLevel:
  901. break;
  902. default:
  903. throw new BsonInternalException("Invalid ContextType.");
  904. }
  905. _context.HasElements = true;
  906. }
  907. private void WriteQuotedString(string value)
  908. {
  909. _textWriter.Write("\"");
  910. _textWriter.Write(EscapedString(value));
  911. _textWriter.Write("\"");
  912. }
  913. }
  914. }