#region Header /** * JsonReader.cs * Stream-like access to JSON text. * * The authors disclaim copyright to this source code. For more details, see * the COPYING file included with this distribution. **/ #endregion using System; using System.Collections.Generic; using System.IO; using System.Text; namespace LitJson { public enum JsonToken { None, ObjectStart, PropertyName, ObjectEnd, ArrayStart, ArrayEnd, Int, Long, Double, String, Boolean, Null } public class JsonReader { #region Fields private static IDictionary> parse_table; private Stack automaton_stack; private int current_input; private int current_symbol; private bool end_of_json; private bool end_of_input; private Lexer lexer; private bool parser_in_string; private bool parser_return; private bool read_started; private TextReader reader; private bool reader_is_owned; private bool skip_non_members; private object token_value; private JsonToken token; #endregion #region Public Properties public bool AllowComments { get { return lexer.AllowComments; } set { lexer.AllowComments = value; } } public bool AllowSingleQuotedStrings { get { return lexer.AllowSingleQuotedStrings; } set { lexer.AllowSingleQuotedStrings = value; } } public bool SkipNonMembers { get { return skip_non_members; } set { skip_non_members = value; } } public bool EndOfInput { get { return end_of_input; } } public bool EndOfJson { get { return end_of_json; } } public JsonToken Token { get { return token; } } public object Value { get { return token_value; } } #endregion #region Constructors static JsonReader () { PopulateParseTable (); } public JsonReader (string json_text) : this (new StringReader (json_text), true) { } public JsonReader (TextReader reader) : this (reader, false) { } private JsonReader (TextReader reader, bool owned) { if (reader == null) throw new ArgumentNullException ("reader"); parser_in_string = false; parser_return = false; read_started = false; automaton_stack = new Stack (); automaton_stack.Push ((int) ParserToken.End); automaton_stack.Push ((int) ParserToken.Text); lexer = new Lexer (reader); end_of_input = false; end_of_json = false; skip_non_members = true; this.reader = reader; reader_is_owned = owned; } #endregion #region Static Methods private static void PopulateParseTable () { // See section A.2. of the manual for details parse_table = new Dictionary> (); TableAddRow (ParserToken.Array); TableAddCol (ParserToken.Array, '[', '[', (int) ParserToken.ArrayPrime); TableAddRow (ParserToken.ArrayPrime); TableAddCol (ParserToken.ArrayPrime, '"', (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddCol (ParserToken.ArrayPrime, '[', (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddCol (ParserToken.ArrayPrime, ']', ']'); TableAddCol (ParserToken.ArrayPrime, '{', (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Number, (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.True, (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.False, (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Null, (int) ParserToken.Value, (int) ParserToken.ValueRest, ']'); TableAddRow (ParserToken.Object); TableAddCol (ParserToken.Object, '{', '{', (int) ParserToken.ObjectPrime); TableAddRow (ParserToken.ObjectPrime); TableAddCol (ParserToken.ObjectPrime, '"', (int) ParserToken.Pair, (int) ParserToken.PairRest, '}'); TableAddCol (ParserToken.ObjectPrime, '}', '}'); TableAddRow (ParserToken.Pair); TableAddCol (ParserToken.Pair, '"', (int) ParserToken.String, ':', (int) ParserToken.Value); TableAddRow (ParserToken.PairRest); TableAddCol (ParserToken.PairRest, ',', ',', (int) ParserToken.Pair, (int) ParserToken.PairRest); TableAddCol (ParserToken.PairRest, '}', (int) ParserToken.Epsilon); TableAddRow (ParserToken.String); TableAddCol (ParserToken.String, '"', '"', (int) ParserToken.CharSeq, '"'); TableAddRow (ParserToken.Text); TableAddCol (ParserToken.Text, '[', (int) ParserToken.Array); TableAddCol (ParserToken.Text, '{', (int) ParserToken.Object); TableAddRow (ParserToken.Value); TableAddCol (ParserToken.Value, '"', (int) ParserToken.String); TableAddCol (ParserToken.Value, '[', (int) ParserToken.Array); TableAddCol (ParserToken.Value, '{', (int) ParserToken.Object); TableAddCol (ParserToken.Value, (int) ParserToken.Number, (int) ParserToken.Number); TableAddCol (ParserToken.Value, (int) ParserToken.True, (int) ParserToken.True); TableAddCol (ParserToken.Value, (int) ParserToken.False, (int) ParserToken.False); TableAddCol (ParserToken.Value, (int) ParserToken.Null, (int) ParserToken.Null); TableAddRow (ParserToken.ValueRest); TableAddCol (ParserToken.ValueRest, ',', ',', (int) ParserToken.Value, (int) ParserToken.ValueRest); TableAddCol (ParserToken.ValueRest, ']', (int) ParserToken.Epsilon); } private static void TableAddCol (ParserToken row, int col, params int[] symbols) { parse_table[(int) row].Add (col, symbols); } private static void TableAddRow (ParserToken rule) { parse_table.Add ((int) rule, new Dictionary ()); } #endregion #region Private Methods private void ProcessNumber (string number) { if (number.IndexOf ('.') != -1 || number.IndexOf ('e') != -1 || number.IndexOf ('E') != -1) { double n_double; if (Double.TryParse (number, out n_double)) { token = JsonToken.Double; token_value = n_double; return; } } int n_int32; if (Int32.TryParse (number, out n_int32)) { token = JsonToken.Int; token_value = n_int32; return; } long n_int64; if (Int64.TryParse (number, out n_int64)) { token = JsonToken.Long; token_value = n_int64; return; } // Shouldn't happen, but just in case, return something token = JsonToken.Int; token_value = 0; } private void ProcessSymbol () { if (current_symbol == '[') { token = JsonToken.ArrayStart; parser_return = true; } else if (current_symbol == ']') { token = JsonToken.ArrayEnd; parser_return = true; } else if (current_symbol == '{') { token = JsonToken.ObjectStart; parser_return = true; } else if (current_symbol == '}') { token = JsonToken.ObjectEnd; parser_return = true; } else if (current_symbol == '"') { if (parser_in_string) { parser_in_string = false; parser_return = true; } else { if (token == JsonToken.None) token = JsonToken.String; parser_in_string = true; } } else if (current_symbol == (int) ParserToken.CharSeq) { token_value = lexer.StringValue; } else if (current_symbol == (int) ParserToken.False) { token = JsonToken.Boolean; token_value = false; parser_return = true; } else if (current_symbol == (int) ParserToken.Null) { token = JsonToken.Null; parser_return = true; } else if (current_symbol == (int) ParserToken.Number) { ProcessNumber (lexer.StringValue); parser_return = true; } else if (current_symbol == (int) ParserToken.Pair) { token = JsonToken.PropertyName; } else if (current_symbol == (int) ParserToken.True) { token = JsonToken.Boolean; token_value = true; parser_return = true; } } private bool ReadToken () { if (end_of_input) return false; lexer.NextToken (); if (lexer.EndOfInput) { Close (); return false; } current_input = lexer.Token; return true; } #endregion public void Close () { if (end_of_input) return; end_of_input = true; end_of_json = true; if (reader_is_owned) reader.Close (); reader = null; } public bool Read () { if (end_of_input) return false; if (end_of_json) { end_of_json = false; automaton_stack.Clear (); automaton_stack.Push ((int) ParserToken.End); automaton_stack.Push ((int) ParserToken.Text); } parser_in_string = false; parser_return = false; token = JsonToken.None; token_value = null; if (! read_started) { read_started = true; if (! ReadToken ()) return false; } int[] entry_symbols; while (true) { if (parser_return) { if (automaton_stack.Peek () == (int) ParserToken.End) end_of_json = true; return true; } current_symbol = automaton_stack.Pop (); ProcessSymbol (); if (current_symbol == current_input) { if (! ReadToken ()) { if (automaton_stack.Peek () != (int) ParserToken.End) throw new JsonException ( "Input doesn't evaluate to proper JSON text"); if (parser_return) return true; return false; } continue; } try { entry_symbols = parse_table[current_symbol][current_input]; } catch (KeyNotFoundException e) { throw new JsonException ((ParserToken) current_input, e); } if (entry_symbols[0] == (int) ParserToken.Epsilon) continue; for (int i = entry_symbols.Length - 1; i >= 0; i--) automaton_stack.Push (entry_symbols[i]); } } } }