comparison Implab/Formats/JSON/JsonReader.cs @ 229:5f7a3e1d32b9 v2

JsonXmlReader performance tuning JsonScanner now operates strings and doesn't parses number and literals. Added SerializationHelpers to common serialize/deserialize operations
author cin
date Tue, 12 Sep 2017 19:07:42 +0300
parents Implab/Formats/JSON/JsonParser.cs@6fa235c5a760
children
comparison
equal deleted inserted replaced
228:6fa235c5a760 229:5f7a3e1d32b9
1 using System;
2 using System.Diagnostics;
3 using System.IO;
4 using Implab.Automaton;
5 using Implab.Automaton.RegularExpressions;
6 using System.Linq;
7 using Implab.Components;
8 using System.Collections.Generic;
9 using System.Text;
10 using System.Globalization;
11
12 namespace Implab.Formats.Json {
13 /// <summary>
14 /// Pull парсер JSON данных.
15 /// </summary>
16 /// <remarks>
17 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>,
18 /// оно означает текущий уровень вложенности объектов, однако закрывающий
19 /// элемент объекта и массива имеет уровень меньше, чем сам объект.
20 /// <code>
21 /// { // Level = 1
22 /// "name" : "Peter", // Level = 1
23 /// "address" : { // Level = 2
24 /// city : "Stern" // Level = 2
25 /// } // Level = 1
26 /// } // Level = 0
27 /// </code>
28 /// </remarks>
29 public class JsonReader : Disposable {
30
31 enum MemberContext {
32 MemberName,
33 MemberValue
34 }
35
36 #region Parser rules
37 struct ParserContext {
38 readonly int[,] m_dfa;
39 int m_state;
40
41 readonly JsonElementContext m_elementContext;
42
43 public ParserContext(int[,] dfa, int state, JsonElementContext context) {
44 m_dfa = dfa;
45 m_state = state;
46 m_elementContext = context;
47 }
48
49 public bool Move(JsonTokenType token) {
50 var next = m_dfa[m_state, (int)token];
51 if (next == AutomatonConst.UNREACHABLE_STATE)
52 return false;
53 m_state = next;
54 return true;
55 }
56
57 public JsonElementContext ElementContext {
58 get { return m_elementContext; }
59 }
60 }
61
62 static readonly ParserContext _jsonContext;
63 static readonly ParserContext _objectContext;
64 static readonly ParserContext _arrayContext;
65
66 static JsonReader() {
67
68 var valueExpression = MakeToken(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String);
69 var memberExpression = MakeToken(JsonTokenType.String).Cat(MakeToken(JsonTokenType.NameSeparator)).Cat(valueExpression);
70
71 var objectExpression = memberExpression
72 .Cat(
73 MakeToken(JsonTokenType.ValueSeparator)
74 .Cat(memberExpression)
75 .EClosure()
76 )
77 .Optional()
78 .Cat(MakeToken(JsonTokenType.EndObject))
79 .End();
80
81 var arrayExpression = valueExpression
82 .Cat(
83 MakeToken(JsonTokenType.ValueSeparator)
84 .Cat(valueExpression)
85 .EClosure()
86 )
87 .Optional()
88 .Cat(MakeToken(JsonTokenType.EndArray))
89 .End();
90
91 var jsonExpression = valueExpression.End();
92
93 _jsonContext = CreateParserContext(jsonExpression, JsonElementContext.None);
94 _objectContext = CreateParserContext(objectExpression, JsonElementContext.Object);
95 _arrayContext = CreateParserContext(arrayExpression, JsonElementContext.Array);
96 }
97
98 static Token MakeToken(params JsonTokenType[] input) {
99 return Token.New( input.Select(t => (int)t).ToArray() );
100 }
101
102 static ParserContext CreateParserContext(Token expr, JsonElementContext context) {
103
104 var dfa = new DFATable();
105 var builder = new RegularExpressionVisitor(dfa);
106 expr.Accept(builder);
107 builder.BuildDFA();
108
109 return new ParserContext(dfa.CreateTransitionTable(), dfa.InitialState, context);
110 }
111
112 #endregion
113
114 readonly JsonScanner m_scanner;
115 // json starts from the value context and may content even a single literal
116 MemberContext m_memberContext = MemberContext.MemberValue;
117
118 JsonElementType m_elementType;
119 object m_elementValue;
120 string m_memberName = String.Empty;
121
122 Stack<ParserContext> m_stack = new Stack<ParserContext>();
123 ParserContext m_context = _jsonContext;
124
125 /// <summary>
126 /// Создает новый парсер на основе строки, содержащей JSON
127 /// </summary>
128 /// <param name="text"></param>
129 JsonReader(JsonScanner scanner) {
130 m_scanner = scanner;
131 }
132
133 public int Level {
134 get { return m_stack.Count; }
135 }
136
137 /// <summary>
138 /// Тип текущего элемента на котором стоит парсер.
139 /// </summary>
140 public JsonElementType ElementType {
141 get { return m_elementType; }
142 }
143
144 /// <summary>
145 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда
146 /// пустая строка.
147 /// </summary>
148 public string ElementName {
149 get { return m_memberName; }
150 }
151
152 /// <summary>
153 /// Значение элемента. Только для элементов типа <see cref="JsonElementType.Value"/>, для остальных <c>null</c>
154 /// </summary>
155 public object ElementValue {
156 get { return m_elementValue; }
157 }
158
159 /// <summary>
160 /// Читает слеюудущий объект из потока
161 /// </summary>
162 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns>
163 public bool Read() {
164 string tokenValue;
165 JsonTokenType tokenType;
166
167 m_memberName = String.Empty;
168
169 while (m_scanner.ReadToken(out tokenValue, out tokenType)) {
170 if(!m_context.Move(tokenType))
171 UnexpectedToken(tokenValue, tokenType);
172
173 switch (tokenType) {
174 case JsonTokenType.BeginObject:
175 m_stack.Push(m_context);
176 m_context = _objectContext;
177
178 m_elementValue = null;
179 m_memberContext = MemberContext.MemberName;
180 m_elementType = JsonElementType.BeginObject;
181 return true;
182 case JsonTokenType.EndObject:
183 if (m_stack.Count == 0)
184 UnexpectedToken(tokenValue, tokenType);
185 m_context = m_stack.Pop();
186
187 m_elementValue = null;
188 m_elementType = JsonElementType.EndObject;
189 return true;
190 case JsonTokenType.BeginArray:
191 m_stack.Push(m_context);
192 m_context = _arrayContext;
193
194 m_elementValue = null;
195 m_memberContext = MemberContext.MemberValue;
196 m_elementType = JsonElementType.BeginArray;
197 return true;
198 case JsonTokenType.EndArray:
199 if (m_stack.Count == 0)
200 UnexpectedToken(tokenValue, tokenType);
201 m_context = m_stack.Pop();
202
203 m_elementValue = null;
204 m_elementType = JsonElementType.EndArray;
205 return true;
206 case JsonTokenType.String:
207 if (m_memberContext == MemberContext.MemberName) {
208 m_memberName = tokenValue;
209 break;
210 }
211 m_elementType = JsonElementType.Value;
212 m_elementValue = tokenValue;
213 return true;
214 case JsonTokenType.Number:
215 m_elementType = JsonElementType.Value;
216 m_elementValue = double.Parse(tokenValue, CultureInfo.InvariantCulture);
217 return true;
218 case JsonTokenType.Literal:
219 m_elementType = JsonElementType.Value;
220 m_elementValue = ParseLiteral(tokenValue);
221 return true;
222 case JsonTokenType.NameSeparator:
223 m_memberContext = MemberContext.MemberValue;
224 break;
225 case JsonTokenType.ValueSeparator:
226 m_memberContext = m_context.ElementContext == JsonElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue;
227 break;
228 default:
229 UnexpectedToken(tokenValue, tokenType);
230 break;
231 }
232 }
233 if (m_context.ElementContext != JsonElementContext.None)
234 throw new ParserException("Unexpedted end of data");
235
236 Eof = true;
237
238 return false;
239 }
240
241 object ParseLiteral(string literal) {
242 switch (literal) {
243 case "null":
244 return null;
245 case "false":
246 return false;
247 case "true":
248 return true;
249 default:
250 UnexpectedToken(literal, JsonTokenType.Literal);
251 return null; // avoid compliler error
252 }
253 }
254
255 void UnexpectedToken(object value, JsonTokenType tokenType) {
256 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value));
257 }
258
259
260 /// <summary>
261 /// Признак конца потока
262 /// </summary>
263 public bool Eof {
264 get;
265 private set;
266 }
267
268 protected override void Dispose(bool disposing) {
269 if (disposing)
270 m_scanner.Dispose();
271 }
272
273 /// <summary>
274 /// Переходит в конец текущего объекта.
275 /// </summary>
276 public void SeekElementEnd() {
277 var level = Level - 1;
278
279 Debug.Assert(level >= 0);
280
281 while (Level != level)
282 Read();
283 }
284
285 public static JsonReader Create(string file, Encoding encoding) {
286 return new JsonReader(JsonTextScanner.Create(file, encoding));
287 }
288
289 public static JsonReader Create(string file) {
290 return new JsonReader(JsonTextScanner.Create(file));
291 }
292
293 public static JsonReader Create(Stream stream, Encoding encoding) {
294 return new JsonReader(JsonTextScanner.Create(stream, encoding));
295 }
296
297 public static JsonReader Create(Stream stream) {
298 return new JsonReader(JsonTextScanner.Create(stream));
299 }
300
301 public static JsonReader Create(TextReader reader) {
302 return new JsonReader(JsonTextScanner.Create(reader));
303 }
304
305 public static JsonReader ParseString(string data) {
306 return new JsonReader(JsonStringScanner.Create(data));
307 }
308
309 public static JsonReader ParseString(string data, int offset, int length) {
310 return new JsonReader(JsonStringScanner.Create(data, offset, length));
311 }
312
313 public static JsonReader ParseString(char[] data, int offset, int lenght) {
314 return new JsonReader(JsonStringScanner.Create(data, offset, lenght));
315 }
316 }
317
318 }