##// END OF EJS Templates
Added Skip method to JSON parser to skip contents of the current node
Added Skip method to JSON parser to skip contents of the current node

File last commit:

r62:62b440d46313 default
r62:62b440d46313 default
Show More
JSONParser.cs
264 lines | 10.5 KiB | text/x-csharp | CSharpLexer
using Implab;
using Implab.Parsing;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Implab.JSON {
/// <summary>
/// internal
/// </summary>
public struct JSONParserContext {
public string memberName;
public JSONElementContext elementContext;
}
/// <summary>
/// Pull парсер JSON данных.
/// </summary>
public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable {
enum MemberContext {
MemberName,
MemberValue
}
static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet;
static readonly DFAStateDescriptior[] _jsonDFA;
static readonly DFAStateDescriptior[] _objectDFA;
static readonly DFAStateDescriptior[] _arrayDFA;
static JSONParser() {
var jsonExpression = Token.New(JsonTokenType.BeginObject, JsonTokenType.BeginArray).Tag(0);
var valueExpression = Token.New(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
var memberExpression = Token.New(JsonTokenType.String).Cat(Token.New(JsonTokenType.NameSeparator)).Cat(valueExpression);
var objectExpression = memberExpression
.Cat(
Token.New(JsonTokenType.ValueSeparator)
.Cat(memberExpression)
.EClosure()
)
.Optional()
.Cat(Token.New(JsonTokenType.EndObject))
.Tag(0);
var arrayExpression = valueExpression
.Cat(
Token.New(JsonTokenType.ValueSeparator)
.Cat(valueExpression)
.EClosure()
)
.Optional()
.Cat(Token.New(JsonTokenType.EndArray))
.Tag(0);
_jsonDFA = BuildDFA(jsonExpression).States;
_objectDFA = BuildDFA(objectExpression).States;
_arrayDFA = BuildDFA(arrayExpression).States;
}
static EDFADefinition<JsonTokenType> BuildDFA(Token expr) {
var builder = new DFABuilder();
var dfa = new EDFADefinition<JsonTokenType>(_alphabet);
expr.Accept(builder);
builder.BuildDFA(dfa);
return dfa;
}
JSONScanner m_scanner;
MemberContext m_memberContext;
JSONElementType m_elementType;
object m_elementValue;
/// <summary>
/// Создает новый парсер на основе строки, содержащей JSON
/// </summary>
/// <param name="text"></param>
public JSONParser(string text)
: base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) {
Safe.ArgumentNotEmpty(text, "text");
m_scanner = new JSONScanner();
m_scanner.Feed(text.ToCharArray());
}
/// <summary>
/// Создает новый экземпляр парсера, на основе текстового потока.
/// </summary>
/// <param name="reader">Текстовый поток.</param>
/// <param name="dispose">Признак того, что парсер должен конролировать время жизни входного потока.</param>
public JSONParser(TextReader reader, bool dispose)
: base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) {
Safe.ArgumentNotNull(reader, "reader");
m_scanner = new JSONScanner();
m_scanner.Feed(reader, dispose);
}
/// <summary>
/// Тип текущего элемента на котором стоит парсер.
/// </summary>
public JSONElementType ElementType {
get { return m_elementType; }
}
/// <summary>
/// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
/// пустая строка.
/// </summary>
public string ElementName {
get { return m_context.info.memberName; }
}
/// <summary>
/// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c>
/// </summary>
public object ElementValue {
get { return m_elementValue; }
}
/// <summary>
/// Читает слеюудущий объект из потока
/// </summary>
/// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
public bool Read() {
if (m_context.current == UNREACHEBLE_STATE)
throw new InvalidOperationException("The parser is in invalid state");
object tokenValue;
JsonTokenType tokenType;
m_context.info.memberName = String.Empty;
while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
Move((int)tokenType);
if (m_context.current == UNREACHEBLE_STATE)
UnexpectedToken(tokenValue, tokenType);
switch (tokenType) {
case JsonTokenType.BeginObject:
Switch(
_objectDFA,
INITIAL_STATE,
new JSONParserContext {
memberName = m_context.info.memberName,
elementContext = JSONElementContext.Object
}
);
m_elementValue = null;
m_memberContext = MemberContext.MemberName;
m_elementType = JSONElementType.BeginObject;
return true;
case JsonTokenType.EndObject:
Restore();
m_elementValue = null;
m_elementType = JSONElementType.EndObject;
return true;
case JsonTokenType.BeginArray:
Switch(
_arrayDFA,
INITIAL_STATE,
new JSONParserContext {
memberName = m_context.info.memberName,
elementContext = JSONElementContext.Array
}
);
m_elementValue = null;
m_memberContext = MemberContext.MemberValue;
m_elementType = JSONElementType.BeginArray;
return true;
case JsonTokenType.EndArray:
Restore();
m_elementValue = null;
m_elementType = JSONElementType.EndArray;
return true;
case JsonTokenType.String:
if (m_memberContext == MemberContext.MemberName) {
m_context.info.memberName = (string)tokenValue;
break;
} else {
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 = ParseLiteral((string)tokenValue);
return true;
case JsonTokenType.NameSeparator:
m_memberContext = MemberContext.MemberValue;
break;
case JsonTokenType.ValueSeparator:
m_memberContext = m_context.info.elementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
break;
default:
UnexpectedToken(tokenValue, tokenType);
break;
}
}
if (m_context.info.elementContext != JSONElementContext.None)
throw new ParserException("Unexpedted end of data");
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 {
return m_scanner.EOF;
}
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
m_scanner.Dispose();
}
}
/// <summary>
/// Освобождает парсер и связанный с ним сканнер.
/// </summary>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~JSONParser() {
Dispose(false);
}
public void Skip() {
var level = Level-1;
Debug.Assert(level >= 0);
while (Level != level)
Read();
}
}
}