Mercurial > pub > ImplabNet
view Implab/Formats/JSON/JSONParser.cs @ 166:b84cdbe82e7f ref20160224
sync
author | cin |
---|---|
date | Mon, 29 Feb 2016 18:41:01 +0300 |
parents | e227e78d72e4 |
children | 92d5278d1b10 |
line wrap: on
line source
using System; using System.Diagnostics; using System.IO; using Implab.Automaton; using Implab.Automaton.RegularExpressions; using System.Linq; using Implab.Components; namespace Implab.Formats.JSON { /// <summary> /// internal /// </summary> public struct JSONParserContext { public string memberName; public JSONElementContext elementContext; } /// <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 JSONParser : Disposable { enum MemberContext { MemberName, MemberValue } struct ParserContext { DFAStateDescriptior<object> } static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet; static readonly DFAStateDescriptior<object>[] _jsonDFA; static readonly int _jsonDFAInitialState; static readonly DFAStateDescriptior<object>[] _objectDFA; static readonly int _objectDFAInitialState; static readonly DFAStateDescriptior<object>[] _arrayDFA; static readonly int _arrayDFAInitialState; static JSONParser() { var valueExpression = Token(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); var memberExpression = Token(JsonTokenType.String).Cat(Token(JsonTokenType.NameSeparator)).Cat(valueExpression); var objectExpression = memberExpression .Cat( Token(JsonTokenType.ValueSeparator) .Cat(memberExpression) .EClosure() ) .Optional() .Cat(Token(JsonTokenType.EndObject)) .Tag(null); var arrayExpression = valueExpression .Cat( Token(JsonTokenType.ValueSeparator) .Cat(valueExpression) .EClosure() ) .Optional() .Cat(Token(JsonTokenType.EndArray)) .Tag(null); var jsonExpression = valueExpression.Tag(null); _jsonDFA = CreateDFA(jsonExpression).GetTransitionTable(); _objectDFA = CreateDFA(objectExpression).GetTransitionTable(); _arrayDFA = CreateDFA(arrayExpression).GetTransitionTable(); } static Token<object> Token(params JsonTokenType[] input) { return Token<object>.New(input.Select(t => _alphabet.Translate(t)).ToArray()); } static RegularDFADefinition<JsonTokenType,object> CreateDFA(Token<object> expr) { var builder = new RegularDFABuilder<object>(); var dfa = new RegularDFADefinition<JsonTokenType,object>(_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; } 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 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(); } } }