Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel de Icaza <miguel@gnome.org>2015-03-06 17:47:56 +0300
committerMiguel de Icaza <miguel@gnome.org>2015-03-06 17:47:56 +0300
commit48992d4b3f8b568be17180372160d2f3e03b8ccb (patch)
treee871d315a5872dcc69b3c98d4f502e749ae0cfc7 /mcs/class/System.Json
parent0a968edad3def3e32f4756f3b9e1893b546277d5 (diff)
parentd65e93b74fd5623881e561dd0be240003a7d6c54 (diff)
Merge pull request #1155 from steffen-kiess/json-string
Fix System.Json string handling and make order of JsonObject entries deterministic
Diffstat (limited to 'mcs/class/System.Json')
-rw-r--r--mcs/class/System.Json/System.Json/JsonObject.cs7
-rw-r--r--mcs/class/System.Json/System.Json/JsonPrimitive.cs2
-rw-r--r--mcs/class/System.Json/System.Json/JsonValue.cs44
-rw-r--r--mcs/class/System.Json/Test/System.Json/JsonValueTest.cs79
4 files changed, 125 insertions, 7 deletions
diff --git a/mcs/class/System.Json/System.Json/JsonObject.cs b/mcs/class/System.Json/System.Json/JsonObject.cs
index 33177b1c5f2..91366a2243a 100644
--- a/mcs/class/System.Json/System.Json/JsonObject.cs
+++ b/mcs/class/System.Json/System.Json/JsonObject.cs
@@ -12,11 +12,12 @@ namespace System.Json
{
public class JsonObject : JsonValue, IDictionary<string, JsonValue>, ICollection<JsonPair>
{
- Dictionary<string, JsonValue> map;
+ // Use SortedDictionary to make result of ToString() deterministic
+ SortedDictionary<string, JsonValue> map;
public JsonObject (params JsonPair [] items)
{
- map = new Dictionary<string, JsonValue> ();
+ map = new SortedDictionary<string, JsonValue> (StringComparer.Ordinal);
if (items != null)
AddRange (items);
@@ -27,7 +28,7 @@ namespace System.Json
if (items == null)
throw new ArgumentNullException ("items");
- map = new Dictionary<string, JsonValue> ();
+ map = new SortedDictionary<string, JsonValue> (StringComparer.Ordinal);
AddRange (items);
}
diff --git a/mcs/class/System.Json/System.Json/JsonPrimitive.cs b/mcs/class/System.Json/System.Json/JsonPrimitive.cs
index 5d47eb4a4e6..24f51ed560a 100644
--- a/mcs/class/System.Json/System.Json/JsonPrimitive.cs
+++ b/mcs/class/System.Json/System.Json/JsonPrimitive.cs
@@ -161,6 +161,8 @@ namespace System.Json
case JsonType.String:
if (value is string || value == null)
return (string) value;
+ if (value is char)
+ return value.ToString ();
throw new NotImplementedException ("GetFormattedString from value type " + value.GetType ());
case JsonType.Number:
string s;
diff --git a/mcs/class/System.Json/System.Json/JsonValue.cs b/mcs/class/System.Json/System.Json/JsonValue.cs
index d703edd5512..ac3f723e2ad 100644
--- a/mcs/class/System.Json/System.Json/JsonValue.cs
+++ b/mcs/class/System.Json/System.Json/JsonValue.cs
@@ -197,13 +197,37 @@ namespace System.Json
throw new InvalidOperationException ();
}
+ // Characters which have to be escaped:
+ // - Required by JSON Spec: Control characters, '"' and '\\'
+ // - Broken surrogates to make sure the JSON string is valid Unicode
+ // (and can be encoded as UTF8)
+ // - JSON does not require U+2028 and U+2029 to be escaped, but
+ // JavaScript does require this:
+ // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character/9168133#9168133
+ // - '/' also does not have to be escaped, but escaping it when
+ // preceeded by a '<' avoids problems with JSON in HTML <script> tags
+ bool NeedEscape (string src, int i) {
+ char c = src [i];
+ return c < 32 || c == '"' || c == '\\'
+ // Broken lead surrogate
+ || (c >= '\uD800' && c <= '\uDBFF' &&
+ (i == src.Length - 1 || src [i + 1] < '\uDC00' || src [i + 1] > '\uDFFF'))
+ // Broken tail surrogate
+ || (c >= '\uDC00' && c <= '\uDFFF' &&
+ (i == 0 || src [i - 1] < '\uD800' || src [i - 1] > '\uDBFF'))
+ // To produce valid JavaScript
+ || c == '\u2028' || c == '\u2029'
+ // Escape "</" for <script> tags
+ || (c == '/' && i > 0 && src [i - 1] == '<');
+ }
+
internal string EscapeString (string src)
{
if (src == null)
return null;
for (int i = 0; i < src.Length; i++)
- if (src [i] == '"' || src [i] == '\\') {
+ if (NeedEscape (src, i)) {
var sb = new StringBuilder ();
if (i > 0)
sb.Append (src, 0, i);
@@ -216,10 +240,22 @@ namespace System.Json
{
int start = cur;
for (int i = cur; i < src.Length; i++)
- if (src [i] == '"' || src [i] == '\\') {
+ if (NeedEscape (src, i)) {
sb.Append (src, start, i - start);
- sb.Append ('\\');
- sb.Append (src [i]);
+ switch (src [i]) {
+ case '\b': sb.Append ("\\b"); break;
+ case '\f': sb.Append ("\\f"); break;
+ case '\n': sb.Append ("\\n"); break;
+ case '\r': sb.Append ("\\r"); break;
+ case '\t': sb.Append ("\\t"); break;
+ case '\"': sb.Append ("\\\""); break;
+ case '\\': sb.Append ("\\\\"); break;
+ case '/': sb.Append ("\\/"); break;
+ default:
+ sb.Append ("\\u");
+ sb.Append (((int) src [i]).ToString ("x04"));
+ break;
+ }
start = i + 1;
}
sb.Append (src, start, src.Length - start);
diff --git a/mcs/class/System.Json/Test/System.Json/JsonValueTest.cs b/mcs/class/System.Json/Test/System.Json/JsonValueTest.cs
index 02dd106dfd8..15d5bd82b38 100644
--- a/mcs/class/System.Json/Test/System.Json/JsonValueTest.cs
+++ b/mcs/class/System.Json/Test/System.Json/JsonValueTest.cs
@@ -46,6 +46,26 @@ namespace MonoTests.System
Assert.AreEqual (str, "[1, 2, 3, null]");
}
+ // Test that we correctly serialize JsonObject with null elements.
+ [Test]
+ public void ToStringOnJsonObjectWithNulls () {
+ var j = JsonValue.Load (new StringReader ("{\"a\":null,\"b\":2}"));
+ Assert.AreEqual (2, j.Count, "itemcount");
+ Assert.AreEqual (JsonType.Object, j.JsonType, "type");
+ var str = j.ToString ();
+ Assert.AreEqual (str, "{\"a\": null, \"b\": 2}");
+ }
+
+ [Test]
+ public void JsonObjectOrder () {
+ var obj = new JsonObject ();
+ obj["a"] = 1;
+ obj["c"] = 3;
+ obj["b"] = 2;
+ var str = obj.ToString ();
+ Assert.AreEqual (str, "{\"a\": 1, \"b\": 2, \"c\": 3}");
+ }
+
[Test]
public void QuoteEscapeBug_20869 ()
{
@@ -181,5 +201,64 @@ namespace MonoTests.System
Thread.CurrentThread.CurrentCulture = old;
}
}
+
+ // Convert a string to json and parse the string, then compare the result to the original value
+ void CheckString (string str)
+ {
+ var json = new JsonPrimitive (str).ToString ();
+ // Check whether the string is valid Unicode (will throw for broken surrogate pairs)
+ new UTF8Encoding (false, true).GetBytes (json);
+ string jvalue = (string) JsonValue.Parse (json);
+ Assert.AreEqual (str, jvalue);
+ }
+
+ // String handling: http://tools.ietf.org/html/rfc7159#section-7
+ [Test]
+ public void CheckStrings ()
+ {
+ Assert.AreEqual ("\"test\"", new JsonPrimitive ("test").ToString ());
+ // Handling of characters
+ Assert.AreEqual ("\"f\"", new JsonPrimitive ('f').ToString ());
+ Assert.AreEqual ('f', (char) JsonValue.Parse ("\"f\""));
+
+ // Control characters with special escape sequence
+ Assert.AreEqual ("\"\\b\\f\\n\\r\\t\"", new JsonPrimitive ("\b\f\n\r\t").ToString ());
+ // Other characters which must be escaped
+ Assert.AreEqual (@"""\""\\""", new JsonPrimitive ("\"\\").ToString ());
+ // Control characters without special escape sequence
+ for (int i = 0; i < 32; i++)
+ if (i != '\b' && i != '\f' && i != '\n' && i != '\r' && i != '\t')
+ Assert.AreEqual ("\"\\u" + i.ToString ("x04") + "\"", new JsonPrimitive ("" + (char) i).ToString ());
+
+ // JSON does not require U+2028 and U+2029 to be escaped, but
+ // JavaScript does require this:
+ // http://stackoverflow.com/questions/2965293/javascript-parse-error-on-u2028-unicode-character/9168133#9168133
+ Assert.AreEqual ("\"\\u2028\\u2029\"", new JsonPrimitive ("\u2028\u2029").ToString ());
+
+ // '/' also does not have to be escaped, but escaping it when
+ // preceeded by a '<' avoids problems with JSON in HTML <script> tags
+ Assert.AreEqual ("\"<\\/\"", new JsonPrimitive ("</").ToString ());
+ // Don't escape '/' in other cases as this makes the JSON hard to read
+ Assert.AreEqual ("\"/bar\"", new JsonPrimitive ("/bar").ToString ());
+ Assert.AreEqual ("\"foo/bar\"", new JsonPrimitive ("foo/bar").ToString ());
+
+ CheckString ("test\b\f\n\r\t\"\\/</\0x");
+ for (int i = 0; i < 65536; i++)
+ CheckString ("x" + ((char) i));
+
+ // Check broken surrogate pairs
+ CheckString ("\ud800");
+ CheckString ("x\ud800");
+ CheckString ("\udfff\ud800");
+ CheckString ("\ude03\ud912");
+ CheckString ("\uc000\ubfff");
+ CheckString ("\udfffx");
+ // Valid strings should not be escaped:
+ Assert.AreEqual ("\"\ud7ff\"", new JsonPrimitive ("\ud7ff").ToString ());
+ Assert.AreEqual ("\"\ue000\"", new JsonPrimitive ("\ue000").ToString ());
+ Assert.AreEqual ("\"\ud800\udc00\"", new JsonPrimitive ("\ud800\udc00").ToString ());
+ Assert.AreEqual ("\"\ud912\ude03\"", new JsonPrimitive ("\ud912\ude03").ToString ());
+ Assert.AreEqual ("\"\udbff\udfff\"", new JsonPrimitive ("\udbff\udfff").ToString ());
+ }
}
}