2 // JsonValueTest.cs: Tests for JSonValue
4 // Copyright 2011 Xamarin, Inc.
14 using System.Globalization;
15 using System.Threading;
17 namespace MonoTests.System
20 public class JsonValueTests {
21 // Tests that a trailing comma is allowed in dictionary definitions
23 public void LoadWithTrailingComma ()
25 var j = JsonValue.Load (new StringReader ("{ \"a\": \"b\",}"));
26 Assert.AreEqual (1, j.Count, "itemcount");
27 Assert.AreEqual (JsonType.String, j ["a"].JsonType, "type");
28 Assert.AreEqual ("b", (string) j ["a"], "value");
30 JsonValue.Parse ("[{ \"a\": \"b\",}]");
34 public void LoadWithTrailingComma2 ()
36 JsonValue.Parse ("[{ \"a\": \"b\",}]");
39 // Test that we correctly serialize JsonArray with null elements.
41 public void ToStringOnJsonArrayWithNulls () {
42 var j = JsonValue.Load (new StringReader ("[1,2,3,null]"));
43 Assert.AreEqual (4, j.Count, "itemcount");
44 Assert.AreEqual (JsonType.Array, j.JsonType, "type");
45 var str = j.ToString ();
46 Assert.AreEqual (str, "[1, 2, 3, null]");
49 // Test that we correctly serialize JsonObject with null elements.
51 public void ToStringOnJsonObjectWithNulls () {
52 var j = JsonValue.Load (new StringReader ("{\"a\":null,\"b\":2}"));
53 Assert.AreEqual (2, j.Count, "itemcount");
54 Assert.AreEqual (JsonType.Object, j.JsonType, "type");
55 var str = j.ToString ();
56 Assert.AreEqual (str, "{\"a\": null, \"b\": 2}");
60 public void JsonObjectOrder () {
61 var obj = new JsonObject ();
65 var str = obj.ToString ();
66 Assert.AreEqual (str, "{\"a\": 1, \"b\": 2, \"c\": 3}");
70 public void QuoteEscapeBug_20869 ()
72 Assert.AreEqual ((new JsonPrimitive ("\"\"")).ToString (), "\"\\\"\\\"\"");
75 void ExpectError (string s)
79 Assert.Fail ("Expected ArgumentException for `" + s + "'");
80 } catch (ArgumentException) {
84 // Test whether an exception is thrown for invalid JSON
86 public void CheckErrors ()
96 ExpectError (@"[1,]");
98 //ExpectError (@"{""a"":1,}"); // Not valid JSON, allowed anyway
101 // Parse a json string and compare to the expected value
102 void CheckDouble (double expected, string json)
104 double jvalue = (double) JsonValue.Parse (json);
105 Assert.AreEqual (expected, jvalue);
108 // Convert a number to json and parse the string, then compare the result to the original value
109 void CheckDouble (double number)
111 double jvalue = (double) JsonValue.Parse (new JsonPrimitive (number).ToString ());
112 Assert.AreEqual (number, jvalue); // should be exactly the same
116 public void CheckNumbers ()
118 CheckDouble (0, "0");
119 CheckDouble (0, "-0");
120 CheckDouble (0, "0.00");
121 CheckDouble (0, "-0.00");
122 CheckDouble (1, "1");
123 CheckDouble (1.1, "1.1");
124 CheckDouble (-1, "-1");
125 CheckDouble (-1.1, "-1.1");
126 CheckDouble (1e-10, "1e-10");
127 CheckDouble (1e+10, "1e+10");
128 CheckDouble (1e-30, "1e-30");
129 CheckDouble (1e+30, "1e+30");
131 CheckDouble (1, "\"1\"");
132 CheckDouble (1.1, "\"1.1\"");
133 CheckDouble (-1, "\"-1\"");
134 CheckDouble (-1.1, "\"-1.1\"");
136 CheckDouble (double.NaN, "\"NaN\"");
137 CheckDouble (double.PositiveInfinity, "\"Infinity\"");
138 CheckDouble (double.NegativeInfinity, "\"-Infinity\"");
141 ExpectError ("Infinity");
142 ExpectError ("-Infinity");
144 Assert.AreEqual ("1.1", new JsonPrimitive (1.1).ToString ());
145 Assert.AreEqual ("-1.1", new JsonPrimitive (-1.1).ToString ());
146 Assert.AreEqual ("1E-20", new JsonPrimitive (1e-20).ToString ());
147 Assert.AreEqual ("1E+20", new JsonPrimitive (1e+20).ToString ());
148 Assert.AreEqual ("1E-30", new JsonPrimitive (1e-30).ToString ());
149 Assert.AreEqual ("1E+30", new JsonPrimitive (1e+30).ToString ());
150 Assert.AreEqual ("\"NaN\"", new JsonPrimitive (double.NaN).ToString ());
151 Assert.AreEqual ("\"Infinity\"", new JsonPrimitive (double.PositiveInfinity).ToString ());
152 Assert.AreEqual ("\"-Infinity\"", new JsonPrimitive (double.NegativeInfinity).ToString ());
154 Assert.AreEqual ("1E-30", JsonValue.Parse ("1e-30").ToString ());
155 Assert.AreEqual ("1E+30", JsonValue.Parse ("1e+30").ToString ());
167 CheckDouble (3.1415926535897932384626433);
168 CheckDouble (3.1415926535897932384626433e-20);
169 CheckDouble (3.1415926535897932384626433e+20);
170 CheckDouble (double.NaN);
171 CheckDouble (double.PositiveInfinity);
172 CheckDouble (double.NegativeInfinity);
173 CheckDouble (double.MinValue);
174 CheckDouble (double.MaxValue);
176 // A number which needs 17 digits (see http://stackoverflow.com/questions/6118231/why-do-i-need-17-significant-digits-and-not-16-to-represent-a-double)
177 CheckDouble (18014398509481982.0);
179 // Values around the smallest positive decimal value
180 CheckDouble (1.123456789e-29);
181 CheckDouble (1.123456789e-28);
183 CheckDouble (1.1E-29, "0.000000000000000000000000000011");
184 // This is being parsed as a decimal and rounded to 1e-28, even though it can be more accurately be represented by a double
185 //CheckDouble (1.1E-28, "0.00000000000000000000000000011");
188 // Retry the test with different locales
190 public void CheckNumbersCulture ()
192 CultureInfo old = Thread.CurrentThread.CurrentCulture;
194 Thread.CurrentThread.CurrentCulture = new CultureInfo ("en");
196 Thread.CurrentThread.CurrentCulture = new CultureInfo ("fr");
198 Thread.CurrentThread.CurrentCulture = new CultureInfo ("de");
201 Thread.CurrentThread.CurrentCulture = old;
205 // Convert a string to json and parse the string, then compare the result to the original value
206 void CheckString (string str)
208 var json = new JsonPrimitive (str).ToString ();
209 // Check whether the string is valid Unicode (will throw for broken surrogate pairs)
210 new UTF8Encoding (false, true).GetBytes (json);
211 string jvalue = (string) JsonValue.Parse (json);
212 Assert.AreEqual (str, jvalue);
215 // String handling: http://tools.ietf.org/html/rfc7159#section-7
217 public void CheckStrings ()
219 Assert.AreEqual ("\"test\"", new JsonPrimitive ("test").ToString ());
220 // Handling of characters
221 Assert.AreEqual ("\"f\"", new JsonPrimitive ('f').ToString ());
222 Assert.AreEqual ('f', (char) JsonValue.Parse ("\"f\""));
224 // Control characters with special escape sequence
225 Assert.AreEqual ("\"\\b\\f\\n\\r\\t\"", new JsonPrimitive ("\b\f\n\r\t").ToString ());
226 // Other characters which must be escaped
227 Assert.AreEqual (@"""\""\\""", new JsonPrimitive ("\"\\").ToString ());
228 // Control characters without special escape sequence
229 for (int i = 0; i < 32; i++)
230 if (i != '\b' && i != '\f' && i != '\n' && i != '\r' && i != '\t')
231 Assert.AreEqual ("\"\\u" + i.ToString ("x04") + "\"", new JsonPrimitive ("" + (char) i).ToString ());
233 // JSON does not require U+2028 and U+2029 to be escaped, but
234 // JavaScript does require this:
235 // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character/9168133#9168133
236 Assert.AreEqual ("\"\\u2028\\u2029\"", new JsonPrimitive ("\u2028\u2029").ToString ());
238 // '/' also does not have to be escaped, but escaping it when
239 // preceeded by a '<' avoids problems with JSON in HTML <script> tags
240 Assert.AreEqual ("\"<\\/\"", new JsonPrimitive ("</").ToString ());
241 // Don't escape '/' in other cases as this makes the JSON hard to read
242 Assert.AreEqual ("\"/bar\"", new JsonPrimitive ("/bar").ToString ());
243 Assert.AreEqual ("\"foo/bar\"", new JsonPrimitive ("foo/bar").ToString ());
245 CheckString ("test\b\f\n\r\t\"\\/</\0x");
246 for (int i = 0; i < 65536; i++)
247 CheckString ("x" + ((char) i));
249 // Check broken surrogate pairs
250 CheckString ("\ud800");
251 CheckString ("x\ud800");
252 CheckString ("\udfff\ud800");
253 CheckString ("\ude03\ud912");
254 CheckString ("\uc000\ubfff");
255 CheckString ("\udfffx");
256 // Valid strings should not be escaped:
257 Assert.AreEqual ("\"\ud7ff\"", new JsonPrimitive ("\ud7ff").ToString ());
258 Assert.AreEqual ("\"\ue000\"", new JsonPrimitive ("\ue000").ToString ());
259 Assert.AreEqual ("\"\ud800\udc00\"", new JsonPrimitive ("\ud800\udc00").ToString ());
260 Assert.AreEqual ("\"\ud912\ude03\"", new JsonPrimitive ("\ud912\ude03").ToString ());
261 Assert.AreEqual ("\"\udbff\udfff\"", new JsonPrimitive ("\udbff\udfff").ToString ());