JsonReader.cs
318 lines
| 11.9 KiB
| text/x-csharp
|
CSharpLexer
cin
|
r230 | 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]; | ||||
cin
|
r236 | if (next == AutomatonConst.UnreachableState) | ||
cin
|
r230 | 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; | ||||
cin
|
r236 | string m_elementValue; | ||
cin
|
r230 | 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> | ||||
cin
|
r236 | public string ElementValue { | ||
cin
|
r230 | 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; | ||||
cin
|
r236 | m_elementValue = tokenValue; | ||
cin
|
r230 | return true; | ||
case JsonTokenType.Literal: | ||||
m_elementType = JsonElementType.Value; | ||||
cin
|
r236 | m_elementValue = tokenValue == "null" ? null : tokenValue; | ||
cin
|
r230 | 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)); | ||||
} | ||||
} | ||||
} | ||||