JsonWriter.cs 31 KB

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