/* * $Id: JSONReader.cs 9 2007-05-27 10:47:07Z spocke $ * * Copyright © 2007, Moxiecode Systems AB, All rights reserved. */ using System; using System.IO; using System.Text; using System.Collections; using System.Collections.Generic; namespace Moxiecode.Plupload.Utils { class Stack { private List items; public Stack() { items = new List(); } public void Push(object item) { items.Add(item); } public object Pop() { object item = items[items.Count - 1]; items.RemoveAt(items.Count - 1); return item; } } /// /// /// public enum JsonLocation { /// InArray, /// InObject, /// Normal } /// /// /// public enum JsonToken { /// Boolean, /// Integer, /// String, /// Null, /// Float, /// StartArray, /// EndArray, /// PropertyName, /// StartObject, /// EndObject } /// /// Description of JSONReader. /// public class JsonReader { private TextReader reader; private JsonToken token; private object val; private JsonLocation location; private Stack lastLocations; private bool needProp; /// /// /// /// public JsonReader(TextReader reader) { this.reader = reader; this.val = null; this.token = JsonToken.Null; this.location = JsonLocation.Normal; this.lastLocations = new Stack(); this.needProp = false; } public static object ParseJson(String json) { JsonReader reader = new JsonReader(new StringReader(json)); return reader.ReadValue(); } /// /// /// public JsonLocation Location { get { return location; } } /// /// /// public JsonToken TokenType { get { return this.token; } } /// /// /// public object Value { get { return this.val; } } /// /// /// /// public bool Read() { int chr = this.reader.Read(); if (chr != -1) { switch ((char) chr) { case '[': this.lastLocations.Push(this.location); this.location = JsonLocation.InArray; this.token = JsonToken.StartArray; this.val = null; this.ReadAway(); return true; case ']': this.location = (JsonLocation)this.lastLocations.Pop(); this.token = JsonToken.EndArray; this.val = null; this.ReadAway(); if (this.location == JsonLocation.InObject) this.needProp = true; return true; case '{': this.lastLocations.Push(this.location); this.location = JsonLocation.InObject; this.needProp = true; this.token = JsonToken.StartObject; this.val = null; this.ReadAway(); return true; case '}': this.location = (JsonLocation) this.lastLocations.Pop(); this.token = JsonToken.EndObject; this.val = null; this.ReadAway(); if (this.location == JsonLocation.InObject) this.needProp = true; return true; // String case '"': case '\'': return this.ReadString((char) chr); // Null case 'n': return this.ReadNull(); // Bool case 't': case 'f': return this.ReadBool((char) chr); default: // Is number if (Char.IsNumber((char) chr) || (char) chr == '-' || (char) chr == '.') return this.ReadNumber((char) chr); return true; } } return false; } /// /// /// /// public override string ToString() { switch (this.token) { case JsonToken.Boolean: return "[Boolean] = " + ((bool) this.Value ? "true" : "false"); case JsonToken.EndArray: return "[EndArray]"; case JsonToken.EndObject: return "[EndObject]"; case JsonToken.Float: return "[Float] = " + Convert.ToDouble(this.Value); case JsonToken.Integer: return "[Integer] = " + ((int) this.Value); case JsonToken.Null: return "[Null]"; case JsonToken.StartArray: return "[StartArray]"; case JsonToken.StartObject: return "[StartObject]"; case JsonToken.String: return "[String]" + (string) this.Value; case JsonToken.PropertyName: return "[PropertyName]" + (string) this.Value; } return base.ToString(); } #region private methods private bool ReadString(char quote) { StringBuilder buff = new StringBuilder(); this.token = JsonToken.String; bool endString = false; int chr; while ((chr = this.reader.Peek()) != -1) { switch (chr) { case '\\': // Read away slash chr = this.reader.Read(); // Read escape code chr = this.reader.Read(); switch (chr) { case 't': buff.Append('\t'); break; case 'b': buff.Append('\b'); break; case 'f': buff.Append('\f'); break; case 'r': buff.Append('\r'); break; case 'n': buff.Append('\n'); break; case 'u': buff.Append((char) Convert.ToInt32(ReadLen(4), 16)); break; default: buff.Append((char) chr); break; } break; case '\'': case '"': if (chr == quote) endString = true; chr = this.reader.Read(); if (chr != -1 && chr != quote) buff.Append((char) chr); break; default: buff.Append((char) this.reader.Read()); break; } // String terminated if (endString) break; } this.ReadAway(); this.val = buff.ToString(); // Needed a property if (this.needProp) { this.token = JsonToken.PropertyName; this.needProp = false; return true; } if (this.location == JsonLocation.InObject && !this.needProp) this.needProp = true; return true; } private bool ReadNull() { this.token = JsonToken.Null; this.val = null; this.ReadAway(3); // ull this.ReadAway(); if (this.location == JsonLocation.InObject && !this.needProp) this.needProp = true; return true; } private bool ReadNumber(char start) { StringBuilder buff = new StringBuilder(); int chr; bool isFloat = false; this.token = JsonToken.Integer; buff.Append(start); while ((chr = this.reader.Peek()) != -1) { if (Char.IsNumber((char) chr) || (char) chr == '-' || (char) chr == '.') { if (((char) chr) == '.') isFloat = true; buff.Append((char) this.reader.Read()); } else break; } this.ReadAway(); if (isFloat) { this.token = JsonToken.Float; this.val = Convert.ToDouble(buff.ToString().Replace('.', ',')); } else this.val = Convert.ToInt32(buff.ToString()); if (this.location == JsonLocation.InObject && !this.needProp) this.needProp = true; return true; } private bool ReadBool(char chr) { this.token = JsonToken.Boolean; this.val = chr == 't'; if (chr == 't') this.ReadAway(3); // rue else this.ReadAway(4); // alse this.ReadAway(); if (this.location == JsonLocation.InObject && !this.needProp) this.needProp = true; return true; } private void ReadAway() { int chr; while ((chr = this.reader.Peek()) != -1) { if (chr != ':' && chr != ',' && !Char.IsWhiteSpace((char) chr)) break; this.reader.Read(); } } private string ReadLen(int num) { StringBuilder buff = new StringBuilder(); int chr; for (int i=0; i) { ((Dictionary)cur)[key] = this.Value; } else if (cur is List) ((List) cur).Add(this.Value); else return this.Value; break; case JsonToken.PropertyName: key = (string) this.Value; break; case JsonToken.StartArray: case JsonToken.StartObject: if (this.TokenType == JsonToken.StartObject) { obj = new Dictionary(); } else { obj = new List(); } if (cur is Dictionary) { ((Dictionary)cur)[key] = obj; } else if (cur is List) { ((List)cur).Add(obj); } parents.Push(cur); cur = obj; break; case JsonToken.EndArray: case JsonToken.EndObject: obj = parents.Pop(); if (obj != null) cur = obj; break; } } return cur; } #endregion } }