486 lines
9.7 KiB
C#
486 lines
9.7 KiB
C#
/*
|
|
* $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<object> items;
|
|
|
|
public Stack() {
|
|
items = new List<object>();
|
|
}
|
|
|
|
public void Push(object item) {
|
|
items.Add(item);
|
|
}
|
|
|
|
public object Pop() {
|
|
object item = items[items.Count - 1];
|
|
|
|
items.RemoveAt(items.Count - 1);
|
|
|
|
return item;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public enum JsonLocation {
|
|
/// <summary> </summary>
|
|
InArray,
|
|
|
|
/// <summary> </summary>
|
|
InObject,
|
|
|
|
/// <summary> </summary>
|
|
Normal
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public enum JsonToken {
|
|
/// <summary> </summary>
|
|
Boolean,
|
|
|
|
/// <summary> </summary>
|
|
Integer,
|
|
|
|
/// <summary> </summary>
|
|
String,
|
|
|
|
/// <summary> </summary>
|
|
Null,
|
|
|
|
/// <summary> </summary>
|
|
Float,
|
|
|
|
/// <summary> </summary>
|
|
StartArray,
|
|
|
|
/// <summary> </summary>
|
|
EndArray,
|
|
|
|
/// <summary> </summary>
|
|
PropertyName,
|
|
|
|
/// <summary> </summary>
|
|
StartObject,
|
|
|
|
/// <summary> </summary>
|
|
EndObject
|
|
}
|
|
|
|
/// <summary>
|
|
/// Description of JSONReader.
|
|
/// </summary>
|
|
public class JsonReader {
|
|
private TextReader reader;
|
|
private JsonToken token;
|
|
private object val;
|
|
private JsonLocation location;
|
|
private Stack lastLocations;
|
|
private bool needProp;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="reader"></param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public JsonLocation Location {
|
|
get { return location; }
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public JsonToken TokenType {
|
|
get {
|
|
return this.token;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public object Value {
|
|
get {
|
|
return this.val;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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<num && (chr = this.reader.Read()) != -1; i++)
|
|
buff.Append((char) chr);
|
|
|
|
return buff.ToString();
|
|
}
|
|
|
|
private void ReadAway(int num) {
|
|
for (int i=0; i<num && this.reader.Read() != -1; i++) ;
|
|
}
|
|
|
|
private object ReadValue() {
|
|
Stack parents = new Stack();
|
|
object cur = null;
|
|
string key = null;
|
|
object obj;
|
|
|
|
while (this.Read()) {
|
|
switch (this.TokenType) {
|
|
case JsonToken.Boolean:
|
|
case JsonToken.Integer:
|
|
case JsonToken.String:
|
|
case JsonToken.Float:
|
|
case JsonToken.Null:
|
|
if (cur is Dictionary<string, object>) {
|
|
((Dictionary<string, object>)cur)[key] = this.Value;
|
|
} else if (cur is List<object>)
|
|
((List<object>) 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<string, object>();
|
|
} else {
|
|
obj = new List<object>();
|
|
}
|
|
|
|
if (cur is Dictionary<string, object>) {
|
|
((Dictionary<string, object>)cur)[key] = obj;
|
|
} else if (cur is List<object>) {
|
|
((List<object>)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
|
|
}
|
|
}
|