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