|
|
using System;
|
|
|
using System.Diagnostics;
|
|
|
using System.IO;
|
|
|
using Implab.Automaton;
|
|
|
using Implab.Automaton.RegularExpressions;
|
|
|
using System.Linq;
|
|
|
using Implab.Components;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Text;
|
|
|
using System.Globalization;
|
|
|
|
|
|
namespace Implab.Formats.Json {
|
|
|
/// <summary>
|
|
|
/// Pull парсер JSON данных.
|
|
|
/// </summary>
|
|
|
/// <remarks>
|
|
|
/// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
|
|
|
/// оно означает текущий уровень вложенности объектов, однако закрывающий
|
|
|
/// элемент объекта и массива имеет уровень меньше, чем сам объект.
|
|
|
/// <code>
|
|
|
/// { // Level = 1
|
|
|
/// "name" : "Peter", // Level = 1
|
|
|
/// "address" : { // Level = 2
|
|
|
/// city : "Stern" // Level = 2
|
|
|
/// } // Level = 1
|
|
|
/// } // Level = 0
|
|
|
/// </code>
|
|
|
/// </remarks>
|
|
|
public class JsonReader : Disposable {
|
|
|
|
|
|
enum MemberContext {
|
|
|
MemberName,
|
|
|
MemberValue
|
|
|
}
|
|
|
|
|
|
#region Parser rules
|
|
|
struct ParserContext {
|
|
|
readonly int[,] m_dfa;
|
|
|
int m_state;
|
|
|
|
|
|
readonly JsonElementContext m_elementContext;
|
|
|
|
|
|
public ParserContext(int[,] dfa, int state, JsonElementContext context) {
|
|
|
m_dfa = dfa;
|
|
|
m_state = state;
|
|
|
m_elementContext = context;
|
|
|
}
|
|
|
|
|
|
public bool Move(JsonTokenType token) {
|
|
|
var next = m_dfa[m_state, (int)token];
|
|
|
if (next == AutomatonConst.UnreachableState)
|
|
|
return false;
|
|
|
m_state = next;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
public JsonElementContext ElementContext {
|
|
|
get { return m_elementContext; }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static readonly ParserContext _jsonContext;
|
|
|
static readonly ParserContext _objectContext;
|
|
|
static readonly ParserContext _arrayContext;
|
|
|
|
|
|
static JsonReader() {
|
|
|
|
|
|
var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
|
|
|
var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
|
|
|
|
|
|
var objectExpression = memberExpression
|
|
|
.Cat(
|
|
|
MakeToken(JsonTokenType.ValueSeparator)
|
|
|
.Cat(memberExpression)
|
|
|
.EClosure()
|
|
|
)
|
|
|
.Optional()
|
|
|
.Cat(MakeToken(JsonTokenType.EndObject))
|
|
|
.End();
|
|
|
|
|
|
var arrayExpression = valueExpression
|
|
|
.Cat(
|
|
|
MakeToken(JsonTokenType.ValueSeparator)
|
|
|
.Cat(valueExpression)
|
|
|
.EClosure()
|
|
|
)
|
|
|
.Optional()
|
|
|
.Cat(MakeToken(JsonTokenType.EndArray))
|
|
|
.End();
|
|
|
|
|
|
var jsonExpression = valueExpression.End();
|
|
|
|
|
|
_jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
|
|
|
_objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
|
|
|
_arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
|
|
|
}
|
|
|
|
|
|
static Token MakeToken(params JsonTokenType[] input) {
|
|
|
return Token.New( input.Select(t => (int)t).ToArray() );
|
|
|
}
|
|
|
|
|
|
static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
|
|
|
|
|
|
var dfa = new DFATable();
|
|
|
var builder = new RegularExpressionVisitor(dfa);
|
|
|
expr.Accept(builder);
|
|
|
builder.BuildDFA();
|
|
|
|
|
|
return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
readonly JsonScanner m_scanner;
|
|
|
// json starts from the value context and may content even a single literal
|
|
|
MemberContext m_memberContext = MemberContext.MemberValue;
|
|
|
|
|
|
JsonElementType m_elementType;
|
|
|
string m_elementValue;
|
|
|
string m_memberName = String.Empty;
|
|
|
|
|
|
Stack<ParserContext> m_stack = new Stack<ParserContext>();
|
|
|
ParserContext m_context = _jsonContext;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Создает новый парсер на основе строки, содержащей JSON
|
|
|
/// </summary>
|
|
|
/// <param name="text"></param>
|
|
|
JsonReader(JsonScanner scanner) {
|
|
|
m_scanner = scanner;
|
|
|
}
|
|
|
|
|
|
public int Level {
|
|
|
get { return m_stack.Count; }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Тип текущего элемента на котором стоит парсер.
|
|
|
/// </summary>
|
|
|
public JsonElementType ElementType {
|
|
|
get { return m_elementType; }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
|
|
|
/// пустая строка.
|
|
|
/// </summary>
|
|
|
public string ElementName {
|
|
|
get { return m_memberName; }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
|
|
|
/// </summary>
|
|
|
public string ElementValue {
|
|
|
get { return m_elementValue; }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Читает слеюудущий объект из потока
|
|
|
/// </summary>
|
|
|
/// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
|
|
|
public bool Read() {
|
|
|
string tokenValue;
|
|
|
JsonTokenType tokenType;
|
|
|
|
|
|
m_memberName = String.Empty;
|
|
|
|
|
|
while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
|
|
|
if(!m_context.Move(tokenType))
|
|
|
UnexpectedToken(tokenValue, tokenType);
|
|
|
|
|
|
switch (tokenType) {
|
|
|
case JsonTokenType.BeginObject:
|
|
|
m_stack.Push(m_context);
|
|
|
m_context = _objectContext;
|
|
|
|
|
|
m_elementValue = null;
|
|
|
m_memberContext = MemberContext.MemberName;
|
|
|
m_elementType = JsonElementType.BeginObject;
|
|
|
return true;
|
|
|
case JsonTokenType.EndObject:
|
|
|
if (m_stack.Count == 0)
|
|
|
UnexpectedToken(tokenValue, tokenType);
|
|
|
m_context = m_stack.Pop();
|
|
|
|
|
|
m_elementValue = null;
|
|
|
m_elementType = JsonElementType.EndObject;
|
|
|
return true;
|
|
|
case JsonTokenType.BeginArray:
|
|
|
m_stack.Push(m_context);
|
|
|
m_context = _arrayContext;
|
|
|
|
|
|
m_elementValue = null;
|
|
|
m_memberContext = MemberContext.MemberValue;
|
|
|
m_elementType = JsonElementType.BeginArray;
|
|
|
return true;
|
|
|
case JsonTokenType.EndArray:
|
|
|
if (m_stack.Count == 0)
|
|
|
UnexpectedToken(tokenValue, tokenType);
|
|
|
m_context = m_stack.Pop();
|
|
|
|
|
|
m_elementValue = null;
|
|
|
m_elementType = JsonElementType.EndArray;
|
|
|
return true;
|
|
|
case JsonTokenType.String:
|
|
|
if (m_memberContext == MemberContext.MemberName) {
|
|
|
m_memberName = tokenValue;
|
|
|
break;
|
|
|
}
|
|
|
m_elementType = JsonElementType.Value;
|
|
|
m_elementValue = tokenValue;
|
|
|
return true;
|
|
|
case JsonTokenType.Number:
|
|
|
m_elementType = JsonElementType.Value;
|
|
|
m_elementValue = tokenValue;
|
|
|
return true;
|
|
|
case JsonTokenType.Literal:
|
|
|
m_elementType = JsonElementType.Value;
|
|
|
m_elementValue = tokenValue == "null" ? null : tokenValue;
|
|
|
return true;
|
|
|
case JsonTokenType.NameSeparator:
|
|
|
m_memberContext = MemberContext.MemberValue;
|
|
|
break;
|
|
|
case JsonTokenType.ValueSeparator:
|
|
|
m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
|
|
|
break;
|
|
|
default:
|
|
|
UnexpectedToken(tokenValue, tokenType);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (m_context.ElementContext != JsonElementContext.None)
|
|
|
throw new ParserException("Unexpedted end of data");
|
|
|
|
|
|
Eof = true;
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
object ParseLiteral(string literal) {
|
|
|
switch (literal) {
|
|
|
case "null":
|
|
|
return null;
|
|
|
case "false":
|
|
|
return false;
|
|
|
case "true":
|
|
|
return true;
|
|
|
default:
|
|
|
UnexpectedToken(literal, JsonTokenType.Literal);
|
|
|
return null; // avoid compliler error
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void UnexpectedToken(object value, JsonTokenType tokenType) {
|
|
|
throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// Признак конца потока
|
|
|
/// </summary>
|
|
|
public bool Eof {
|
|
|
get;
|
|
|
private set;
|
|
|
}
|
|
|
|
|
|
protected override void Dispose(bool disposing) {
|
|
|
if (disposing)
|
|
|
m_scanner.Dispose();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Переходит в конец текущего объекта.
|
|
|
/// </summary>
|
|
|
public void SeekElementEnd() {
|
|
|
var level = Level - 1;
|
|
|
|
|
|
Debug.Assert(level >= 0);
|
|
|
|
|
|
while (Level != level)
|
|
|
Read();
|
|
|
}
|
|
|
|
|
|
public static JsonReader Create(string file, Encoding encoding) {
|
|
|
return new JsonReader(JsonTextScanner.Create(file, encoding));
|
|
|
}
|
|
|
|
|
|
public static JsonReader Create(string file) {
|
|
|
return new JsonReader(JsonTextScanner.Create(file));
|
|
|
}
|
|
|
|
|
|
public static JsonReader Create(Stream stream, Encoding encoding) {
|
|
|
return new JsonReader(JsonTextScanner.Create(stream, encoding));
|
|
|
}
|
|
|
|
|
|
public static JsonReader Create(Stream stream) {
|
|
|
return new JsonReader(JsonTextScanner.Create(stream));
|
|
|
}
|
|
|
|
|
|
public static JsonReader Create(TextReader reader) {
|
|
|
return new JsonReader(JsonTextScanner.Create(reader));
|
|
|
}
|
|
|
|
|
|
public static JsonReader ParseString(string data) {
|
|
|
return new JsonReader(JsonStringScanner.Create(data));
|
|
|
}
|
|
|
|
|
|
public static JsonReader ParseString(string data, int offset, int length) {
|
|
|
return new JsonReader(JsonStringScanner.Create(data, offset, length));
|
|
|
}
|
|
|
|
|
|
public static JsonReader ParseString(char[] data, int offset, int lenght) {
|
|
|
return new JsonReader(JsonStringScanner.Create(data, offset, lenght));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|