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