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