JsonFormatterTest.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. #region Copyright notice and license
  2. // Protocol Buffers - Google's data interchange format
  3. // Copyright 2008 Google Inc. All rights reserved.
  4. // https://developers.google.com/protocol-buffers/
  5. //
  6. // Redistribution and use in source and binary forms, with or without
  7. // modification, are permitted provided that the following conditions are
  8. // met:
  9. //
  10. // * Redistributions of source code must retain the above copyright
  11. // notice, this list of conditions and the following disclaimer.
  12. // * Redistributions in binary form must reproduce the above
  13. // copyright notice, this list of conditions and the following disclaimer
  14. // in the documentation and/or other materials provided with the
  15. // distribution.
  16. // * Neither the name of Google Inc. nor the names of its
  17. // contributors may be used to endorse or promote products derived from
  18. // this software without specific prior written permission.
  19. //
  20. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. #endregion
  32. using System;
  33. using Google.Protobuf.TestProtos;
  34. using NUnit.Framework;
  35. using UnitTest.Issues.TestProtos;
  36. using Google.Protobuf.WellKnownTypes;
  37. using Google.Protobuf.Reflection;
  38. using static Google.Protobuf.JsonParserTest; // For WrapInQuotes
  39. using System.IO;
  40. using Google.Protobuf.Collections;
  41. namespace Google.Protobuf
  42. {
  43. /// <summary>
  44. /// Tests for the JSON formatter. Note that in these tests, double quotes are replaced with apostrophes
  45. /// for the sake of readability (embedding \" everywhere is painful). See the AssertJson method for details.
  46. /// </summary>
  47. public class JsonFormatterTest
  48. {
  49. [Test]
  50. public void DefaultValues_WhenOmitted()
  51. {
  52. var formatter = new JsonFormatter(new JsonFormatter.Settings(formatDefaultValues: false));
  53. AssertJson("{ }", formatter.Format(new ForeignMessage()));
  54. AssertJson("{ }", formatter.Format(new TestAllTypes()));
  55. AssertJson("{ }", formatter.Format(new TestMap()));
  56. }
  57. [Test]
  58. public void DefaultValues_WhenIncluded()
  59. {
  60. var formatter = new JsonFormatter(new JsonFormatter.Settings(formatDefaultValues: true));
  61. AssertJson("{ 'c': 0 }", formatter.Format(new ForeignMessage()));
  62. }
  63. [Test]
  64. public void AllSingleFields()
  65. {
  66. var message = new TestAllTypes
  67. {
  68. SingleBool = true,
  69. SingleBytes = ByteString.CopyFrom(1, 2, 3, 4),
  70. SingleDouble = 23.5,
  71. SingleFixed32 = 23,
  72. SingleFixed64 = 1234567890123,
  73. SingleFloat = 12.25f,
  74. SingleForeignEnum = ForeignEnum.ForeignBar,
  75. SingleForeignMessage = new ForeignMessage { C = 10 },
  76. SingleImportEnum = ImportEnum.ImportBaz,
  77. SingleImportMessage = new ImportMessage { D = 20 },
  78. SingleInt32 = 100,
  79. SingleInt64 = 3210987654321,
  80. SingleNestedEnum = TestAllTypes.Types.NestedEnum.Foo,
  81. SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 35 },
  82. SinglePublicImportMessage = new PublicImportMessage { E = 54 },
  83. SingleSfixed32 = -123,
  84. SingleSfixed64 = -12345678901234,
  85. SingleSint32 = -456,
  86. SingleSint64 = -12345678901235,
  87. SingleString = "test\twith\ttabs",
  88. SingleUint32 = uint.MaxValue,
  89. SingleUint64 = ulong.MaxValue,
  90. };
  91. var actualText = JsonFormatter.Default.Format(message);
  92. // Fields in numeric order
  93. var expectedText = "{ " +
  94. "'singleInt32': 100, " +
  95. "'singleInt64': '3210987654321', " +
  96. "'singleUint32': 4294967295, " +
  97. "'singleUint64': '18446744073709551615', " +
  98. "'singleSint32': -456, " +
  99. "'singleSint64': '-12345678901235', " +
  100. "'singleFixed32': 23, " +
  101. "'singleFixed64': '1234567890123', " +
  102. "'singleSfixed32': -123, " +
  103. "'singleSfixed64': '-12345678901234', " +
  104. "'singleFloat': 12.25, " +
  105. "'singleDouble': 23.5, " +
  106. "'singleBool': true, " +
  107. "'singleString': 'test\\twith\\ttabs', " +
  108. "'singleBytes': 'AQIDBA==', " +
  109. "'singleNestedMessage': { 'bb': 35 }, " +
  110. "'singleForeignMessage': { 'c': 10 }, " +
  111. "'singleImportMessage': { 'd': 20 }, " +
  112. "'singleNestedEnum': 'FOO', " +
  113. "'singleForeignEnum': 'FOREIGN_BAR', " +
  114. "'singleImportEnum': 'IMPORT_BAZ', " +
  115. "'singlePublicImportMessage': { 'e': 54 }" +
  116. " }";
  117. AssertJson(expectedText, actualText);
  118. }
  119. [Test]
  120. public void RepeatedField()
  121. {
  122. AssertJson("{ 'repeatedInt32': [ 1, 2, 3, 4, 5 ] }",
  123. JsonFormatter.Default.Format(new TestAllTypes { RepeatedInt32 = { 1, 2, 3, 4, 5 } }));
  124. }
  125. [Test]
  126. public void MapField_StringString()
  127. {
  128. AssertJson("{ 'mapStringString': { 'with spaces': 'bar', 'a': 'b' } }",
  129. JsonFormatter.Default.Format(new TestMap { MapStringString = { { "with spaces", "bar" }, { "a", "b" } } }));
  130. }
  131. [Test]
  132. public void MapField_Int32Int32()
  133. {
  134. // The keys are quoted, but the values aren't.
  135. AssertJson("{ 'mapInt32Int32': { '0': 1, '2': 3 } }",
  136. JsonFormatter.Default.Format(new TestMap { MapInt32Int32 = { { 0, 1 }, { 2, 3 } } }));
  137. }
  138. [Test]
  139. public void MapField_BoolBool()
  140. {
  141. // The keys are quoted, but the values aren't.
  142. AssertJson("{ 'mapBoolBool': { 'false': true, 'true': false } }",
  143. JsonFormatter.Default.Format(new TestMap { MapBoolBool = { { false, true }, { true, false } } }));
  144. }
  145. [TestCase(1.0, "1")]
  146. [TestCase(double.NaN, "'NaN'")]
  147. [TestCase(double.PositiveInfinity, "'Infinity'")]
  148. [TestCase(double.NegativeInfinity, "'-Infinity'")]
  149. public void DoubleRepresentations(double value, string expectedValueText)
  150. {
  151. var message = new TestAllTypes { SingleDouble = value };
  152. string actualText = JsonFormatter.Default.Format(message);
  153. string expectedText = "{ 'singleDouble': " + expectedValueText + " }";
  154. AssertJson(expectedText, actualText);
  155. }
  156. [Test]
  157. public void UnknownEnumValueNumeric_SingleField()
  158. {
  159. var message = new TestAllTypes { SingleForeignEnum = (ForeignEnum) 100 };
  160. AssertJson("{ 'singleForeignEnum': 100 }", JsonFormatter.Default.Format(message));
  161. }
  162. [Test]
  163. public void UnknownEnumValueNumeric_RepeatedField()
  164. {
  165. var message = new TestAllTypes { RepeatedForeignEnum = { ForeignEnum.ForeignBaz, (ForeignEnum) 100, ForeignEnum.ForeignFoo } };
  166. AssertJson("{ 'repeatedForeignEnum': [ 'FOREIGN_BAZ', 100, 'FOREIGN_FOO' ] }", JsonFormatter.Default.Format(message));
  167. }
  168. [Test]
  169. public void UnknownEnumValueNumeric_MapField()
  170. {
  171. var message = new TestMap { MapInt32Enum = { { 1, MapEnum.Foo }, { 2, (MapEnum) 100 }, { 3, MapEnum.Bar } } };
  172. AssertJson("{ 'mapInt32Enum': { '1': 'MAP_ENUM_FOO', '2': 100, '3': 'MAP_ENUM_BAR' } }", JsonFormatter.Default.Format(message));
  173. }
  174. [Test]
  175. public void UnknownEnumValue_RepeatedField_AllEntriesUnknown()
  176. {
  177. var message = new TestAllTypes { RepeatedForeignEnum = { (ForeignEnum) 200, (ForeignEnum) 100 } };
  178. AssertJson("{ 'repeatedForeignEnum': [ 200, 100 ] }", JsonFormatter.Default.Format(message));
  179. }
  180. [Test]
  181. [TestCase("a\u17b4b", "a\\u17b4b")] // Explicit
  182. [TestCase("a\u0601b", "a\\u0601b")] // Ranged
  183. [TestCase("a\u0605b", "a\u0605b")] // Passthrough (note lack of double backslash...)
  184. public void SimpleNonAscii(string text, string encoded)
  185. {
  186. var message = new TestAllTypes { SingleString = text };
  187. AssertJson("{ 'singleString': '" + encoded + "' }", JsonFormatter.Default.Format(message));
  188. }
  189. [Test]
  190. public void SurrogatePairEscaping()
  191. {
  192. var message = new TestAllTypes { SingleString = "a\uD801\uDC01b" };
  193. AssertJson("{ 'singleString': 'a\\ud801\\udc01b' }", JsonFormatter.Default.Format(message));
  194. }
  195. [Test]
  196. public void InvalidSurrogatePairsFail()
  197. {
  198. // Note: don't use TestCase for these, as the strings can't be reliably represented
  199. // See http://codeblog.jonskeet.uk/2014/11/07/when-is-a-string-not-a-string/
  200. // Lone low surrogate
  201. var message = new TestAllTypes { SingleString = "a\uDC01b" };
  202. Assert.Throws<ArgumentException>(() => JsonFormatter.Default.Format(message));
  203. // Lone high surrogate
  204. message = new TestAllTypes { SingleString = "a\uD801b" };
  205. Assert.Throws<ArgumentException>(() => JsonFormatter.Default.Format(message));
  206. }
  207. [Test]
  208. [TestCase("foo_bar", "fooBar")]
  209. [TestCase("bananaBanana", "bananaBanana")]
  210. [TestCase("BANANABanana", "bananaBanana")]
  211. public void ToCamelCase(string original, string expected)
  212. {
  213. Assert.AreEqual(expected, JsonFormatter.ToCamelCase(original));
  214. }
  215. [Test]
  216. [TestCase(null, "{ }")]
  217. [TestCase("x", "{ 'fooString': 'x' }")]
  218. [TestCase("", "{ 'fooString': '' }")]
  219. public void Oneof(string fooStringValue, string expectedJson)
  220. {
  221. var message = new TestOneof();
  222. if (fooStringValue != null)
  223. {
  224. message.FooString = fooStringValue;
  225. }
  226. // We should get the same result both with and without "format default values".
  227. var formatter = new JsonFormatter(new JsonFormatter.Settings(false));
  228. AssertJson(expectedJson, formatter.Format(message));
  229. formatter = new JsonFormatter(new JsonFormatter.Settings(true));
  230. AssertJson(expectedJson, formatter.Format(message));
  231. }
  232. [Test]
  233. public void WrapperFormatting_Single()
  234. {
  235. // Just a few examples, handling both classes and value types, and
  236. // default vs non-default values
  237. var message = new TestWellKnownTypes
  238. {
  239. Int64Field = 10,
  240. Int32Field = 0,
  241. BytesField = ByteString.FromBase64("ABCD"),
  242. StringField = ""
  243. };
  244. var expectedJson = "{ 'int64Field': '10', 'int32Field': 0, 'stringField': '', 'bytesField': 'ABCD' }";
  245. AssertJson(expectedJson, JsonFormatter.Default.Format(message));
  246. }
  247. [Test]
  248. public void WrapperFormatting_Message()
  249. {
  250. Assert.AreEqual("\"\"", JsonFormatter.Default.Format(new StringValue()));
  251. Assert.AreEqual("0", JsonFormatter.Default.Format(new Int32Value()));
  252. }
  253. [Test]
  254. public void WrapperFormatting_IncludeNull()
  255. {
  256. // The actual JSON here is very large because there are lots of fields. Just test a couple of them.
  257. var message = new TestWellKnownTypes { Int32Field = 10 };
  258. var formatter = new JsonFormatter(new JsonFormatter.Settings(true));
  259. var actualJson = formatter.Format(message);
  260. Assert.IsTrue(actualJson.Contains("\"int64Field\": null"));
  261. Assert.IsFalse(actualJson.Contains("\"int32Field\": null"));
  262. }
  263. [Test]
  264. public void OutputIsInNumericFieldOrder_NoDefaults()
  265. {
  266. var formatter = new JsonFormatter(new JsonFormatter.Settings(false));
  267. var message = new TestJsonFieldOrdering { PlainString = "p1", PlainInt32 = 2 };
  268. AssertJson("{ 'plainString': 'p1', 'plainInt32': 2 }", formatter.Format(message));
  269. message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
  270. AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
  271. message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
  272. AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
  273. }
  274. [Test]
  275. public void OutputIsInNumericFieldOrder_WithDefaults()
  276. {
  277. var formatter = new JsonFormatter(new JsonFormatter.Settings(true));
  278. var message = new TestJsonFieldOrdering();
  279. AssertJson("{ 'plainString': '', 'plainInt32': 0 }", formatter.Format(message));
  280. message = new TestJsonFieldOrdering { O1Int32 = 5, O2String = "o2", PlainInt32 = 10, PlainString = "plain" };
  281. AssertJson("{ 'plainString': 'plain', 'o2String': 'o2', 'plainInt32': 10, 'o1Int32': 5 }", formatter.Format(message));
  282. message = new TestJsonFieldOrdering { O1String = "", O2Int32 = 0, PlainInt32 = 10, PlainString = "plain" };
  283. AssertJson("{ 'plainString': 'plain', 'o1String': '', 'plainInt32': 10, 'o2Int32': 0 }", formatter.Format(message));
  284. }
  285. [Test]
  286. [TestCase("1970-01-01T00:00:00Z", 0)]
  287. [TestCase("1970-01-01T00:00:00.000000001Z", 1)]
  288. [TestCase("1970-01-01T00:00:00.000000010Z", 10)]
  289. [TestCase("1970-01-01T00:00:00.000000100Z", 100)]
  290. [TestCase("1970-01-01T00:00:00.000001Z", 1000)]
  291. [TestCase("1970-01-01T00:00:00.000010Z", 10000)]
  292. [TestCase("1970-01-01T00:00:00.000100Z", 100000)]
  293. [TestCase("1970-01-01T00:00:00.001Z", 1000000)]
  294. [TestCase("1970-01-01T00:00:00.010Z", 10000000)]
  295. [TestCase("1970-01-01T00:00:00.100Z", 100000000)]
  296. [TestCase("1970-01-01T00:00:00.120Z", 120000000)]
  297. [TestCase("1970-01-01T00:00:00.123Z", 123000000)]
  298. [TestCase("1970-01-01T00:00:00.123400Z", 123400000)]
  299. [TestCase("1970-01-01T00:00:00.123450Z", 123450000)]
  300. [TestCase("1970-01-01T00:00:00.123456Z", 123456000)]
  301. [TestCase("1970-01-01T00:00:00.123456700Z", 123456700)]
  302. [TestCase("1970-01-01T00:00:00.123456780Z", 123456780)]
  303. [TestCase("1970-01-01T00:00:00.123456789Z", 123456789)]
  304. public void TimestampStandalone(string expected, int nanos)
  305. {
  306. Assert.AreEqual(WrapInQuotes(expected), new Timestamp { Nanos = nanos }.ToString());
  307. }
  308. [Test]
  309. public void TimestampStandalone_FromDateTime()
  310. {
  311. // One before and one after the Unix epoch, more easily represented via DateTime.
  312. Assert.AreEqual("\"1673-06-19T12:34:56Z\"",
  313. new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp().ToString());
  314. Assert.AreEqual("\"2015-07-31T10:29:34Z\"",
  315. new DateTime(2015, 7, 31, 10, 29, 34, DateTimeKind.Utc).ToTimestamp().ToString());
  316. }
  317. [Test]
  318. [TestCase(-1, -1)] // Would be valid as duration
  319. [TestCase(1, Timestamp.MaxNanos + 1)]
  320. [TestCase(Timestamp.UnixSecondsAtBclMaxValue + 1, 0)]
  321. [TestCase(Timestamp.UnixSecondsAtBclMinValue - 1, 0)]
  322. public void TimestampStandalone_NonNormalized(long seconds, int nanoseconds)
  323. {
  324. var timestamp = new Timestamp { Seconds = seconds, Nanos = nanoseconds };
  325. Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(timestamp));
  326. }
  327. [Test]
  328. public void TimestampField()
  329. {
  330. var message = new TestWellKnownTypes { TimestampField = new Timestamp() };
  331. AssertJson("{ 'timestampField': '1970-01-01T00:00:00Z' }", JsonFormatter.Default.Format(message));
  332. }
  333. [Test]
  334. [TestCase(0, 0, "0s")]
  335. [TestCase(1, 0, "1s")]
  336. [TestCase(-1, 0, "-1s")]
  337. [TestCase(0, 1, "0.000000001s")]
  338. [TestCase(0, 10, "0.000000010s")]
  339. [TestCase(0, 100, "0.000000100s")]
  340. [TestCase(0, 1000, "0.000001s")]
  341. [TestCase(0, 10000, "0.000010s")]
  342. [TestCase(0, 100000, "0.000100s")]
  343. [TestCase(0, 1000000, "0.001s")]
  344. [TestCase(0, 10000000, "0.010s")]
  345. [TestCase(0, 100000000, "0.100s")]
  346. [TestCase(0, 120000000, "0.120s")]
  347. [TestCase(0, 123000000, "0.123s")]
  348. [TestCase(0, 123400000, "0.123400s")]
  349. [TestCase(0, 123450000, "0.123450s")]
  350. [TestCase(0, 123456000, "0.123456s")]
  351. [TestCase(0, 123456700, "0.123456700s")]
  352. [TestCase(0, 123456780, "0.123456780s")]
  353. [TestCase(0, 123456789, "0.123456789s")]
  354. [TestCase(0, -100000000, "-0.100s")]
  355. [TestCase(1, 100000000, "1.100s")]
  356. [TestCase(-1, -100000000, "-1.100s")]
  357. public void DurationStandalone(long seconds, int nanoseconds, string expected)
  358. {
  359. var json = JsonFormatter.Default.Format(new Duration { Seconds = seconds, Nanos = nanoseconds });
  360. Assert.AreEqual(WrapInQuotes(expected), json);
  361. }
  362. [Test]
  363. [TestCase(1, 2123456789)]
  364. [TestCase(1, -100000000)]
  365. public void DurationStandalone_NonNormalized(long seconds, int nanoseconds)
  366. {
  367. var duration = new Duration { Seconds = seconds, Nanos = nanoseconds };
  368. Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(duration));
  369. }
  370. [Test]
  371. public void DurationField()
  372. {
  373. var message = new TestWellKnownTypes { DurationField = new Duration() };
  374. AssertJson("{ 'durationField': '0s' }", JsonFormatter.Default.Format(message));
  375. }
  376. [Test]
  377. public void StructSample()
  378. {
  379. var message = new Struct
  380. {
  381. Fields =
  382. {
  383. { "a", Value.ForNull() },
  384. { "b", Value.ForBool(false) },
  385. { "c", Value.ForNumber(10.5) },
  386. { "d", Value.ForString("text") },
  387. { "e", Value.ForList(Value.ForString("t1"), Value.ForNumber(5)) },
  388. { "f", Value.ForStruct(new Struct { Fields = { { "nested", Value.ForString("value") } } }) }
  389. }
  390. };
  391. AssertJson("{ 'a': null, 'b': false, 'c': 10.5, 'd': 'text', 'e': [ 't1', 5 ], 'f': { 'nested': 'value' } }", message.ToString());
  392. }
  393. [Test]
  394. [TestCase("foo__bar")]
  395. [TestCase("foo_3_ar")]
  396. [TestCase("fooBar")]
  397. public void FieldMaskInvalid(string input)
  398. {
  399. var mask = new FieldMask { Paths = { input } };
  400. Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(mask));
  401. }
  402. [Test]
  403. public void FieldMaskStandalone()
  404. {
  405. var fieldMask = new FieldMask { Paths = { "", "single", "with_underscore", "nested.field.name", "nested..double_dot" } };
  406. Assert.AreEqual("\",single,withUnderscore,nested.field.name,nested..doubleDot\"", fieldMask.ToString());
  407. // Invalid, but we shouldn't create broken JSON...
  408. fieldMask = new FieldMask { Paths = { "x\\y" } };
  409. Assert.AreEqual(@"""x\\y""", fieldMask.ToString());
  410. }
  411. [Test]
  412. public void FieldMaskField()
  413. {
  414. var message = new TestWellKnownTypes { FieldMaskField = new FieldMask { Paths = { "user.display_name", "photo" } } };
  415. AssertJson("{ 'fieldMaskField': 'user.displayName,photo' }", JsonFormatter.Default.Format(message));
  416. }
  417. // SourceContext is an example of a well-known type with no special JSON handling
  418. [Test]
  419. public void SourceContextStandalone()
  420. {
  421. var message = new SourceContext { FileName = "foo.proto" };
  422. AssertJson("{ 'fileName': 'foo.proto' }", JsonFormatter.Default.Format(message));
  423. }
  424. [Test]
  425. public void AnyWellKnownType()
  426. {
  427. var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(Timestamp.Descriptor)));
  428. var timestamp = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
  429. var any = Any.Pack(timestamp);
  430. AssertJson("{ '@type': 'type.googleapis.com/google.protobuf.Timestamp', 'value': '1673-06-19T12:34:56Z' }", formatter.Format(any));
  431. }
  432. [Test]
  433. public void AnyMessageType()
  434. {
  435. var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
  436. var message = new TestAllTypes { SingleInt32 = 10, SingleNestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 } };
  437. var any = Any.Pack(message);
  438. AssertJson("{ '@type': 'type.googleapis.com/protobuf_unittest.TestAllTypes', 'singleInt32': 10, 'singleNestedMessage': { 'bb': 20 } }", formatter.Format(any));
  439. }
  440. [Test]
  441. public void AnyMessageType_CustomPrefix()
  442. {
  443. var formatter = new JsonFormatter(new JsonFormatter.Settings(false, TypeRegistry.FromMessages(TestAllTypes.Descriptor)));
  444. var message = new TestAllTypes { SingleInt32 = 10 };
  445. var any = Any.Pack(message, "foo.bar/baz");
  446. AssertJson("{ '@type': 'foo.bar/baz/protobuf_unittest.TestAllTypes', 'singleInt32': 10 }", formatter.Format(any));
  447. }
  448. [Test]
  449. public void AnyNested()
  450. {
  451. var registry = TypeRegistry.FromMessages(TestWellKnownTypes.Descriptor, TestAllTypes.Descriptor);
  452. var formatter = new JsonFormatter(new JsonFormatter.Settings(false, registry));
  453. // Nest an Any as the value of an Any.
  454. var doubleNestedMessage = new TestAllTypes { SingleInt32 = 20 };
  455. var nestedMessage = Any.Pack(doubleNestedMessage);
  456. var message = new TestWellKnownTypes { AnyField = Any.Pack(nestedMessage) };
  457. AssertJson("{ 'anyField': { '@type': 'type.googleapis.com/google.protobuf.Any', 'value': { '@type': 'type.googleapis.com/protobuf_unittest.TestAllTypes', 'singleInt32': 20 } } }",
  458. formatter.Format(message));
  459. }
  460. [Test]
  461. public void AnyUnknownType()
  462. {
  463. // The default type registry doesn't have any types in it.
  464. var message = new TestAllTypes();
  465. var any = Any.Pack(message);
  466. Assert.Throws<InvalidOperationException>(() => JsonFormatter.Default.Format(any));
  467. }
  468. [Test]
  469. [TestCase(typeof(BoolValue), true, "true")]
  470. [TestCase(typeof(Int32Value), 32, "32")]
  471. [TestCase(typeof(Int64Value), 32L, "\"32\"")]
  472. [TestCase(typeof(UInt32Value), 32U, "32")]
  473. [TestCase(typeof(UInt64Value), 32UL, "\"32\"")]
  474. [TestCase(typeof(StringValue), "foo", "\"foo\"")]
  475. [TestCase(typeof(FloatValue), 1.5f, "1.5")]
  476. [TestCase(typeof(DoubleValue), 1.5d, "1.5")]
  477. public void Wrappers_Standalone(System.Type wrapperType, object value, string expectedJson)
  478. {
  479. IMessage populated = (IMessage)Activator.CreateInstance(wrapperType);
  480. populated.Descriptor.Fields[WrappersReflection.WrapperValueFieldNumber].Accessor.SetValue(populated, value);
  481. Assert.AreEqual(expectedJson, JsonFormatter.Default.Format(populated));
  482. }
  483. // Sanity tests for WriteValue. Not particularly comprehensive, as it's all covered above already,
  484. // as FormatMessage uses WriteValue.
  485. [TestCase(null, "null")]
  486. [TestCase(1, "1")]
  487. [TestCase(1L, "'1'")]
  488. [TestCase(0.5f, "0.5")]
  489. [TestCase(0.5d, "0.5")]
  490. [TestCase("text", "'text'")]
  491. [TestCase("x\ny", @"'x\ny'")]
  492. [TestCase(ForeignEnum.ForeignBar, "'FOREIGN_BAR'")]
  493. public void WriteValue_Constant(object value, string expectedJson)
  494. {
  495. AssertWriteValue(value, expectedJson);
  496. }
  497. [Test]
  498. public void WriteValue_Timestamp()
  499. {
  500. var value = new DateTime(1673, 6, 19, 12, 34, 56, DateTimeKind.Utc).ToTimestamp();
  501. AssertWriteValue(value, "'1673-06-19T12:34:56Z'");
  502. }
  503. [Test]
  504. public void WriteValue_Message()
  505. {
  506. var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
  507. AssertWriteValue(value, "{ 'singleInt32': 100, 'singleInt64': '3210987654321' }");
  508. }
  509. [Test]
  510. public void WriteValue_List()
  511. {
  512. var value = new RepeatedField<int> { 1, 2, 3 };
  513. AssertWriteValue(value, "[ 1, 2, 3 ]");
  514. }
  515. private static void AssertWriteValue(object value, string expectedJson)
  516. {
  517. var writer = new StringWriter();
  518. JsonFormatter.Default.WriteValue(writer, value);
  519. string actual = writer.ToString();
  520. AssertJson(expectedJson, actual);
  521. }
  522. /// <summary>
  523. /// Checks that the actual JSON is the same as the expected JSON - but after replacing
  524. /// all apostrophes in the expected JSON with double quotes. This basically makes the tests easier
  525. /// to read.
  526. /// </summary>
  527. private static void AssertJson(string expectedJsonWithApostrophes, string actualJson)
  528. {
  529. var expectedJson = expectedJsonWithApostrophes.Replace("'", "\"");
  530. Assert.AreEqual(expectedJson, actualJson);
  531. }
  532. }
  533. }