You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
478 lines
15 KiB
478 lines
15 KiB
#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.Globalization; |
|
using System.IO; |
|
using System.Text; |
|
|
|
|
|
namespace BestHTTP.JSON.LitJson |
|
{ |
|
public enum JsonToken |
|
{ |
|
None, |
|
|
|
ObjectStart, |
|
PropertyName, |
|
ObjectEnd, |
|
|
|
ArrayStart, |
|
ArrayEnd, |
|
|
|
Int, |
|
Long, |
|
Double, |
|
|
|
String, |
|
|
|
Boolean, |
|
Null |
|
} |
|
|
|
|
|
public sealed class JsonReader |
|
{ |
|
#region Fields |
|
private static readonly IDictionary<int, IDictionary<int, int[]>> parse_table; |
|
|
|
private Stack<int> 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 () |
|
{ |
|
parse_table = 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<int> (); |
|
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 IDictionary<int, IDictionary<int, int[]>> PopulateParseTable () |
|
{ |
|
// See section A.2. of the manual for details |
|
IDictionary<int, IDictionary<int, int[]>> parse_table = new Dictionary<int, IDictionary<int, int[]>> (); |
|
|
|
TableAddRow (parse_table, ParserToken.Array); |
|
TableAddCol (parse_table, ParserToken.Array, '[', |
|
'[', |
|
(int) ParserToken.ArrayPrime); |
|
|
|
TableAddRow (parse_table, ParserToken.ArrayPrime); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, '"', |
|
(int) ParserToken.Value, |
|
|
|
(int) ParserToken.ValueRest, |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, '[', |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest, |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, ']', |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, '{', |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest, |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Number, |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest, |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.True, |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest, |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.False, |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest, |
|
']'); |
|
TableAddCol (parse_table, ParserToken.ArrayPrime, (int) ParserToken.Null, |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest, |
|
']'); |
|
|
|
TableAddRow (parse_table, ParserToken.Object); |
|
TableAddCol (parse_table, ParserToken.Object, '{', |
|
'{', |
|
(int) ParserToken.ObjectPrime); |
|
|
|
TableAddRow (parse_table, ParserToken.ObjectPrime); |
|
TableAddCol (parse_table, ParserToken.ObjectPrime, '"', |
|
(int) ParserToken.Pair, |
|
(int) ParserToken.PairRest, |
|
'}'); |
|
TableAddCol (parse_table, ParserToken.ObjectPrime, '}', |
|
'}'); |
|
|
|
TableAddRow (parse_table, ParserToken.Pair); |
|
TableAddCol (parse_table, ParserToken.Pair, '"', |
|
(int) ParserToken.String, |
|
':', |
|
(int) ParserToken.Value); |
|
|
|
TableAddRow (parse_table, ParserToken.PairRest); |
|
TableAddCol (parse_table, ParserToken.PairRest, ',', |
|
',', |
|
(int) ParserToken.Pair, |
|
(int) ParserToken.PairRest); |
|
TableAddCol (parse_table, ParserToken.PairRest, '}', |
|
(int) ParserToken.Epsilon); |
|
|
|
TableAddRow (parse_table, ParserToken.String); |
|
TableAddCol (parse_table, ParserToken.String, '"', |
|
'"', |
|
(int) ParserToken.CharSeq, |
|
'"'); |
|
|
|
TableAddRow (parse_table, ParserToken.Text); |
|
TableAddCol (parse_table, ParserToken.Text, '[', |
|
(int) ParserToken.Array); |
|
TableAddCol (parse_table, ParserToken.Text, '{', |
|
(int) ParserToken.Object); |
|
|
|
TableAddRow (parse_table, ParserToken.Value); |
|
TableAddCol (parse_table, ParserToken.Value, '"', |
|
(int) ParserToken.String); |
|
TableAddCol (parse_table, ParserToken.Value, '[', |
|
(int) ParserToken.Array); |
|
TableAddCol (parse_table, ParserToken.Value, '{', |
|
(int) ParserToken.Object); |
|
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Number, |
|
(int) ParserToken.Number); |
|
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.True, |
|
(int) ParserToken.True); |
|
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.False, |
|
(int) ParserToken.False); |
|
TableAddCol (parse_table, ParserToken.Value, (int) ParserToken.Null, |
|
(int) ParserToken.Null); |
|
|
|
TableAddRow (parse_table, ParserToken.ValueRest); |
|
TableAddCol (parse_table, ParserToken.ValueRest, ',', |
|
',', |
|
(int) ParserToken.Value, |
|
(int) ParserToken.ValueRest); |
|
TableAddCol (parse_table, ParserToken.ValueRest, ']', |
|
(int) ParserToken.Epsilon); |
|
|
|
return parse_table; |
|
} |
|
|
|
private static void TableAddCol (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken row, int col, |
|
params int[] symbols) |
|
{ |
|
parse_table[(int) row].Add (col, symbols); |
|
} |
|
|
|
private static void TableAddRow (IDictionary<int, IDictionary<int, int[]>> parse_table, ParserToken rule) |
|
{ |
|
parse_table.Add ((int) rule, new Dictionary<int, int[]> ()); |
|
} |
|
#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, NumberStyles.Any, CultureInfo.InvariantCulture, out n_double)) { |
|
token = JsonToken.Double; |
|
token_value = n_double; |
|
|
|
return; |
|
} |
|
} |
|
|
|
int n_int32; |
|
if (int.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int32)) { |
|
token = JsonToken.Int; |
|
token_value = n_int32; |
|
|
|
return; |
|
} |
|
|
|
long n_int64; |
|
if (long.TryParse (number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_int64)) { |
|
token = JsonToken.Long; |
|
token_value = n_int64; |
|
|
|
return; |
|
} |
|
|
|
ulong n_uint64; |
|
if (ulong.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out n_uint64)) |
|
{ |
|
token = JsonToken.Long; |
|
token_value = n_uint64; |
|
|
|
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) |
|
{ |
|
using(reader){} |
|
} |
|
|
|
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]); |
|
} |
|
} |
|
|
|
} |
|
}
|
|
|