using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Runtime.Serialization.Json; using System.Text; using JsonPair = System.Collections.Generic.KeyValuePair; namespace System.Json { public abstract class JsonValue : IEnumerable { public static JsonValue Load (Stream stream) { if (stream == null) throw new ArgumentNullException ("stream"); return Load (new StreamReader (stream, true)); } public static JsonValue Load (TextReader textReader) { if (textReader == null) throw new ArgumentNullException ("textReader"); var ret = new JavaScriptReader (textReader, true).Read (); return ToJsonValue (ret); } static IEnumerable> ToJsonPairEnumerable (IEnumerable> kvpc) { foreach (var kvp in kvpc) yield return new KeyValuePair (kvp.Key, ToJsonValue (kvp.Value)); } static IEnumerable ToJsonValueEnumerable (IEnumerable arr) { foreach (var obj in arr) yield return ToJsonValue (obj); } static JsonValue ToJsonValue (object ret) { if (ret == null) return null; var kvpc = ret as IEnumerable>; if (kvpc != null) return new JsonObject (ToJsonPairEnumerable (kvpc)); var arr = ret as IEnumerable; if (arr != null) return new JsonArray (ToJsonValueEnumerable (arr)); if (ret is bool) return new JsonPrimitive ((bool) ret); if (ret is byte) return new JsonPrimitive ((byte) ret); if (ret is char) return new JsonPrimitive ((char) ret); if (ret is decimal) return new JsonPrimitive ((decimal) ret); if (ret is double) return new JsonPrimitive ((double) ret); if (ret is float) return new JsonPrimitive ((float) ret); if (ret is int) return new JsonPrimitive ((int) ret); if (ret is long) return new JsonPrimitive ((long) ret); if (ret is sbyte) return new JsonPrimitive ((sbyte) ret); if (ret is short) return new JsonPrimitive ((short) ret); if (ret is string) return new JsonPrimitive ((string) ret); if (ret is uint) return new JsonPrimitive ((uint) ret); if (ret is ulong) return new JsonPrimitive ((ulong) ret); if (ret is ushort) return new JsonPrimitive ((ushort) ret); if (ret is DateTime) return new JsonPrimitive ((DateTime) ret); if (ret is DateTimeOffset) return new JsonPrimitive ((DateTimeOffset) ret); if (ret is Guid) return new JsonPrimitive ((Guid) ret); if (ret is TimeSpan) return new JsonPrimitive ((TimeSpan) ret); if (ret is Uri) return new JsonPrimitive ((Uri) ret); throw new NotSupportedException (String.Format ("Unexpected parser return type: {0}", ret.GetType ())); } public static JsonValue Parse (string jsonString) { if (jsonString == null) throw new ArgumentNullException ("jsonString"); return Load (new StringReader (jsonString)); } public virtual int Count { get { throw new InvalidOperationException (); } } public abstract JsonType JsonType { get; } public virtual JsonValue this [int index] { get { throw new InvalidOperationException (); } set { throw new InvalidOperationException (); } } public virtual JsonValue this [string key] { get { throw new InvalidOperationException (); } set { throw new InvalidOperationException (); } } public virtual bool ContainsKey (string key) { throw new InvalidOperationException (); } public virtual void Save (Stream stream) { if (stream == null) throw new ArgumentNullException ("stream"); Save (new StreamWriter (stream)); } public virtual void Save (TextWriter textWriter) { if (textWriter == null) throw new ArgumentNullException ("textWriter"); SaveInternal (textWriter); } void SaveInternal (TextWriter w) { switch (JsonType) { case JsonType.Object: w.Write ('{'); bool following = false; foreach (JsonPair pair in ((JsonObject) this)) { if (following) w.Write (", "); w.Write ('\"'); w.Write (EscapeString (pair.Key)); w.Write ("\": "); if (pair.Value == null) w.Write ("null"); else pair.Value.SaveInternal (w); following = true; } w.Write ('}'); break; case JsonType.Array: w.Write ('['); following = false; foreach (JsonValue v in ((JsonArray) this)) { if (following) w.Write (", "); if (v != null) v.SaveInternal (w); else w.Write ("null"); following = true; } w.Write (']'); break; case JsonType.Boolean: w.Write ((bool) this ? "true" : "false"); break; case JsonType.String: w.Write ('"'); w.Write (EscapeString (((JsonPrimitive) this).GetFormattedString ())); w.Write ('"'); break; default: w.Write (((JsonPrimitive) this).GetFormattedString ()); break; } } public override string ToString () { StringWriter sw = new StringWriter (); Save (sw); return sw.ToString (); } IEnumerator IEnumerable.GetEnumerator () { 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