JsonWriter.cs 32 KB

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