Mercurial > pub > ImplabNet
annotate Implab/JSON/JSONParser.cs @ 140:f973c5df9972 v2
fixes
author | cin |
---|---|
date | Fri, 20 Feb 2015 15:58:34 +0300 |
parents | 0349189d2564 |
children | 97fbbf816844 |
rev | line source |
---|---|
55 | 1 using Implab; |
2 using Implab.Parsing; | |
3 using System; | |
4 using System.Collections.Generic; | |
5 using System.Diagnostics; | |
59 | 6 using System.IO; |
55 | 7 using System.Linq; |
8 using System.Text; | |
9 using System.Threading.Tasks; | |
10 | |
11 namespace Implab.JSON { | |
12 /// <summary> | |
13 /// internal | |
14 /// </summary> | |
15 public struct JSONParserContext { | |
16 public string memberName; | |
17 public JSONElementContext elementContext; | |
18 } | |
19 | |
20 /// <summary> | |
21 /// Pull парсер JSON данных. | |
22 /// </summary> | |
70 | 23 /// <remarks> |
24 /// Следует отметить отдельную интерпретацию свойства <see cref="Level"/>, | |
25 /// оно означает текущий уровень вложенности объектов, однако закрывающий | |
26 /// элемент объекта и массива имеет уровень меньше, чем сам объект. | |
27 /// <code> | |
28 /// { // Level = 1 | |
29 /// "name" : "Peter", // Level = 1 | |
30 /// "address" : { // Level = 2 | |
31 /// city : "Stern" // Level = 2 | |
32 /// } // Level = 1 | |
33 /// } // Level = 0 | |
34 /// </code> | |
35 /// </remarks> | |
59 | 36 public class JSONParser : DFAutomaton<JSONParserContext>, IDisposable { |
55 | 37 |
38 enum MemberContext { | |
39 MemberName, | |
40 MemberValue | |
59 | 41 } |
55 | 42 |
43 static readonly EnumAlphabet<JsonTokenType> _alphabet = EnumAlphabet<JsonTokenType>.FullAlphabet; | |
44 static readonly DFAStateDescriptior[] _jsonDFA; | |
45 static readonly DFAStateDescriptior[] _objectDFA; | |
46 static readonly DFAStateDescriptior[] _arrayDFA; | |
47 | |
48 static JSONParser() { | |
140 | 49 |
55 | 50 |
51 var valueExpression = Token.New(JsonTokenType.BeginArray, JsonTokenType.BeginObject, JsonTokenType.Literal, JsonTokenType.Number, JsonTokenType.String); | |
52 var memberExpression = Token.New(JsonTokenType.String).Cat(Token.New(JsonTokenType.NameSeparator)).Cat(valueExpression); | |
140 | 53 |
55 | 54 var objectExpression = memberExpression |
55 .Cat( | |
56 Token.New(JsonTokenType.ValueSeparator) | |
57 .Cat(memberExpression) | |
58 .EClosure() | |
59 ) | |
60 .Optional() | |
61 .Cat(Token.New(JsonTokenType.EndObject)) | |
62 .Tag(0); | |
63 var arrayExpression = valueExpression | |
64 .Cat( | |
65 Token.New(JsonTokenType.ValueSeparator) | |
66 .Cat(valueExpression) | |
67 .EClosure() | |
68 ) | |
69 .Optional() | |
70 .Cat(Token.New(JsonTokenType.EndArray)) | |
71 .Tag(0); | |
72 | |
140 | 73 var jsonExpression = valueExpression.Tag(0); |
74 | |
55 | 75 _jsonDFA = BuildDFA(jsonExpression).States; |
76 _objectDFA = BuildDFA(objectExpression).States; | |
77 _arrayDFA = BuildDFA(arrayExpression).States; | |
78 } | |
79 | |
80 static EDFADefinition<JsonTokenType> BuildDFA(Token expr) { | |
81 var builder = new DFABuilder(); | |
82 var dfa = new EDFADefinition<JsonTokenType>(_alphabet); | |
83 expr.Accept(builder); | |
84 | |
85 builder.BuildDFA(dfa); | |
86 return dfa; | |
87 } | |
88 | |
89 JSONScanner m_scanner; | |
90 MemberContext m_memberContext; | |
91 | |
92 JSONElementType m_elementType; | |
93 object m_elementValue; | |
94 | |
59 | 95 /// <summary> |
96 /// Создает новый парсер на основе строки, содержащей JSON | |
97 /// </summary> | |
98 /// <param name="text"></param> | |
55 | 99 public JSONParser(string text) |
59 | 100 : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { |
55 | 101 Safe.ArgumentNotEmpty(text, "text"); |
102 m_scanner = new JSONScanner(); | |
103 m_scanner.Feed(text.ToCharArray()); | |
104 } | |
105 | |
59 | 106 /// <summary> |
107 /// Создает новый экземпляр парсера, на основе текстового потока. | |
108 /// </summary> | |
109 /// <param name="reader">Текстовый поток.</param> | |
110 /// <param name="dispose">Признак того, что парсер должен конролировать время жизни входного потока.</param> | |
111 public JSONParser(TextReader reader, bool dispose) | |
112 : base(_jsonDFA, INITIAL_STATE, new JSONParserContext { elementContext = JSONElementContext.None, memberName = String.Empty }) { | |
113 Safe.ArgumentNotNull(reader, "reader"); | |
114 m_scanner = new JSONScanner(); | |
115 m_scanner.Feed(reader, dispose); | |
116 } | |
117 | |
118 /// <summary> | |
119 /// Тип текущего элемента на котором стоит парсер. | |
120 /// </summary> | |
55 | 121 public JSONElementType ElementType { |
122 get { return m_elementType; } | |
123 } | |
124 | |
59 | 125 /// <summary> |
126 /// Имя элемента - имя свойства родительского контейнера. Для элементов массивов и корневого всегда | |
127 /// пустая строка. | |
128 /// </summary> | |
55 | 129 public string ElementName { |
130 get { return m_context.info.memberName; } | |
131 } | |
132 | |
59 | 133 /// <summary> |
134 /// Значение элемента. Только для элементов типа <see cref="JSONElementType.Value"/>, для остальных <c>null</c> | |
135 /// </summary> | |
55 | 136 public object ElementValue { |
137 get { return m_elementValue; } | |
138 } | |
139 | |
59 | 140 /// <summary> |
141 /// Читает слеюудущий объект из потока | |
142 /// </summary> | |
143 /// <returns><c>true</c> - операция чтения прошла успешно, <c>false</c> - конец данных</returns> | |
55 | 144 public bool Read() { |
145 if (m_context.current == UNREACHEBLE_STATE) | |
146 throw new InvalidOperationException("The parser is in invalid state"); | |
147 object tokenValue; | |
148 JsonTokenType tokenType; | |
149 m_context.info.memberName = String.Empty; | |
150 while (m_scanner.ReadToken(out tokenValue, out tokenType)) { | |
151 Move((int)tokenType); | |
152 if (m_context.current == UNREACHEBLE_STATE) | |
153 UnexpectedToken(tokenValue, tokenType); | |
154 switch (tokenType) { | |
155 case JsonTokenType.BeginObject: | |
156 Switch( | |
157 _objectDFA, | |
158 INITIAL_STATE, | |
59 | 159 new JSONParserContext { |
55 | 160 memberName = m_context.info.memberName, |
161 elementContext = JSONElementContext.Object | |
162 } | |
163 ); | |
164 m_elementValue = null; | |
165 m_memberContext = MemberContext.MemberName; | |
166 m_elementType = JSONElementType.BeginObject; | |
167 return true; | |
168 case JsonTokenType.EndObject: | |
169 Restore(); | |
170 m_elementValue = null; | |
171 m_elementType = JSONElementType.EndObject; | |
172 return true; | |
173 case JsonTokenType.BeginArray: | |
174 Switch( | |
175 _arrayDFA, | |
176 INITIAL_STATE, | |
177 new JSONParserContext { | |
178 memberName = m_context.info.memberName, | |
179 elementContext = JSONElementContext.Array | |
180 } | |
181 ); | |
182 m_elementValue = null; | |
183 m_memberContext = MemberContext.MemberValue; | |
184 m_elementType = JSONElementType.BeginArray; | |
185 return true; | |
186 case JsonTokenType.EndArray: | |
187 Restore(); | |
188 m_elementValue = null; | |
189 m_elementType = JSONElementType.EndArray; | |
190 return true; | |
191 case JsonTokenType.String: | |
192 if (m_memberContext == MemberContext.MemberName) { | |
193 m_context.info.memberName = (string)tokenValue; | |
194 break; | |
195 } else { | |
196 m_elementType = JSONElementType.Value; | |
197 m_elementValue = tokenValue; | |
198 return true; | |
199 } | |
200 case JsonTokenType.Number: | |
201 m_elementType = JSONElementType.Value; | |
202 m_elementValue = tokenValue; | |
203 return true; | |
204 case JsonTokenType.Literal: | |
205 m_elementType = JSONElementType.Value; | |
206 m_elementValue = ParseLiteral((string)tokenValue); | |
207 return true; | |
208 case JsonTokenType.NameSeparator: | |
209 m_memberContext = MemberContext.MemberValue; | |
210 break; | |
211 case JsonTokenType.ValueSeparator: | |
212 m_memberContext = m_context.info.elementContext == JSONElementContext.Object ? MemberContext.MemberName : MemberContext.MemberValue; | |
213 break; | |
214 default: | |
215 UnexpectedToken(tokenValue, tokenType); | |
216 break; | |
217 } | |
218 } | |
219 if (m_context.info.elementContext != JSONElementContext.None) | |
220 throw new ParserException("Unexpedted end of data"); | |
221 return false; | |
222 } | |
223 | |
224 object ParseLiteral(string literal) { | |
225 switch (literal) { | |
226 case "null": | |
227 return null; | |
59 | 228 case "false": |
55 | 229 return false; |
230 case "true": | |
231 return true; | |
232 default: | |
233 UnexpectedToken(literal, JsonTokenType.Literal); | |
234 return null; // avoid compliler error | |
235 } | |
236 } | |
237 | |
238 void UnexpectedToken(object value, JsonTokenType tokenType) { | |
239 throw new ParserException(String.Format("Unexpected token {0}: '{1}'", tokenType, value)); | |
240 } | |
241 | |
57 | 242 |
59 | 243 /// <summary> |
244 /// Признак конца потока | |
245 /// </summary> | |
57 | 246 public bool EOF { |
247 get { | |
248 return m_scanner.EOF; | |
249 } | |
250 } | |
59 | 251 |
252 protected virtual void Dispose(bool disposing) { | |
253 if (disposing) { | |
254 m_scanner.Dispose(); | |
255 } | |
256 } | |
257 | |
258 /// <summary> | |
259 /// Освобождает парсер и связанный с ним сканнер. | |
260 /// </summary> | |
261 public void Dispose() { | |
262 Dispose(true); | |
263 GC.SuppressFinalize(this); | |
264 } | |
265 | |
266 ~JSONParser() { | |
267 Dispose(false); | |
268 } | |
62
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
269 |
70 | 270 /// <summary> |
271 /// Переходит в конец текущего объекта. | |
272 /// </summary> | |
273 public void SeekElementEnd() { | |
274 var level = Level - 1; | |
62
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
275 |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
276 Debug.Assert(level >= 0); |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
277 |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
278 while (Level != level) |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
279 Read(); |
62b440d46313
Added Skip method to JSON parser to skip contents of the current node
cin
parents:
59
diff
changeset
|
280 } |
55 | 281 } |
282 | |
283 } |