JsonWriter.cs 32 KB

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