Mercurial > pub > ImplabNet
view Implab/Formats/JSON/JsonReader.cs @ 234:8dd666e6b6bf v2
Added implab nuget spec
author | cin |
---|---|
date | Thu, 05 Oct 2017 09:21:23 +0300 |
parents | 5f7a3e1d32b9 |
children |
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; 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.UNREACHABLE_STATE) 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; object 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 object 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 = double.Parse(tokenValue, CultureInfo.InvariantCulture); return true; case JsonTokenType.Literal: m_elementType = JsonElementType.Value; m_elementValue = ParseLiteral(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)); } } }