Mercurial > pub > ImplabNet
annotate Implab/JSON/JSONParser.cs @ 153:b933ec88446e v2
docs
| author | cin |
|---|---|
| date | Fri, 12 Feb 2016 00:59:29 +0300 |
| parents | f973c5df9972 |
| 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 } |
