Mercurial > pub > ImplabNet
annotate Implab/JSON/JSONXmlReader.cs @ 145:706fccb85524 v2
RC: cancellation support for promises + tests
author | cin |
---|---|
date | Sun, 08 Mar 2015 02:52:27 +0300 |
parents | abe260860bd6 |
children |
rev | line source |
---|---|
58 | 1 using Implab; |
2 using Implab.Parsing; | |
3 using System; | |
4 using System.Collections.Generic; | |
63
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
5 using System.Globalization; |
60 | 6 using System.IO; |
58 | 7 using System.Linq; |
8 using System.Text; | |
9 using System.Threading.Tasks; | |
10 using System.Xml; | |
11 | |
60 | 12 namespace Implab.JSON { |
58 | 13 public class JSONXmlReader : XmlReader { |
14 | |
15 enum ValueContext { | |
16 Undefined, | |
17 ElementStart, | |
18 ElementValue, | |
19 ElementEnd, | |
20 ElementEmpty | |
21 } | |
22 | |
23 struct LocalNameContext { | |
24 public string localName; | |
25 public bool isArray; | |
26 } | |
27 | |
28 JSONParser m_parser; | |
29 ValueContext m_valueContext; | |
30 ReadState m_state = ReadState.Initial; | |
31 Stack<LocalNameContext> m_localNameStack = new Stack<LocalNameContext>(); | |
32 LocalNameContext m_localName; | |
33 int m_depthCorrection = 0; | |
34 | |
60 | 35 readonly string m_rootName; |
36 readonly string m_prefix; | |
37 readonly string m_namespaceUri; | |
38 readonly bool m_flattenArrays; | |
39 readonly string m_arrayItemName; | |
40 readonly XmlNameTable m_nameTable; | |
63
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
41 |
64 | 42 JSONXmlReader(JSONParser parser, JSONXmlReaderOptions options) { |
58 | 43 m_parser = parser; |
60 | 44 |
45 if (options != null) { | |
46 m_prefix = options.NodesPrefix ?? String.Empty; | |
47 m_namespaceUri = options.NamespaceURI ?? String.Empty; | |
48 m_rootName = options.RootName ?? "json"; | |
49 m_flattenArrays = options.FlattenArrays; | |
50 m_arrayItemName = options.ArrayItemName ?? "item"; | |
51 m_nameTable = options.NameTable ?? new NameTable(); | |
52 } else { | |
53 m_prefix = String.Empty; | |
54 m_namespaceUri = String.Empty; | |
55 m_rootName = "json"; | |
56 m_flattenArrays = false; | |
57 m_arrayItemName = "item"; | |
58 m_nameTable = new NameTable(); | |
59 } | |
58 | 60 } |
61 | |
62 /// <summary> | |
63 /// Always 0, JSON doesn't support attributes | |
64 /// </summary> | |
65 public override int AttributeCount { | |
66 get { return 0; } | |
67 } | |
68 | |
69 public override string BaseURI { | |
70 get { return String.Empty; } | |
71 } | |
72 | |
73 public override int Depth { | |
74 get { | |
63
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
75 return m_localNameStack.Count + m_depthCorrection; |
58 | 76 } |
77 } | |
78 | |
79 public override bool EOF { | |
80 get { return m_parser.EOF; } | |
81 } | |
82 | |
83 /// <summary> | |
84 /// Always throws an exception | |
85 /// </summary> | |
86 /// <param name="i"></param> | |
87 /// <returns></returns> | |
88 public override string GetAttribute(int i) { | |
89 throw new ArgumentOutOfRangeException(); | |
90 } | |
91 | |
92 /// <summary> | |
93 /// Always returns empty string | |
94 /// </summary> | |
95 /// <param name="name"></param> | |
96 /// <param name="namespaceURI"></param> | |
97 /// <returns></returns> | |
98 public override string GetAttribute(string name, string namespaceURI) { | |
99 return String.Empty; | |
100 } | |
101 | |
102 /// <summary> | |
103 /// Always returns empty string | |
104 /// </summary> | |
105 /// <param name="name"></param> | |
106 /// <returns></returns> | |
107 public override string GetAttribute(string name) { | |
108 return String.Empty; | |
109 } | |
110 | |
111 public override bool IsEmptyElement { | |
112 get { return m_parser.ElementType == JSONElementType.Value && m_valueContext == ValueContext.ElementEmpty; } | |
113 } | |
114 | |
115 public override string LocalName { | |
116 get { return m_localName.localName; } | |
117 } | |
118 | |
119 public override string LookupNamespace(string prefix) { | |
120 if (String.IsNullOrEmpty(prefix) || prefix == m_prefix) | |
121 return m_namespaceUri; | |
122 else | |
123 return String.Empty; | |
124 } | |
125 | |
126 public override bool MoveToAttribute(string name, string ns) { | |
127 return false; | |
128 } | |
129 | |
130 public override bool MoveToAttribute(string name) { | |
131 return false; | |
132 } | |
133 | |
134 public override bool MoveToElement() { | |
135 return false; | |
136 } | |
137 | |
138 public override bool MoveToFirstAttribute() { | |
139 return false; | |
140 } | |
141 | |
142 public override bool MoveToNextAttribute() { | |
143 return false; | |
144 } | |
145 | |
146 public override XmlNameTable NameTable { | |
147 get { return m_nameTable; } | |
148 } | |
149 | |
150 public override string NamespaceURI { | |
151 get { return m_namespaceUri; } | |
152 } | |
153 | |
154 public override XmlNodeType NodeType { | |
155 get { | |
156 switch (m_parser.ElementType) { | |
157 case JSONElementType.BeginObject: | |
158 case JSONElementType.BeginArray: | |
159 return XmlNodeType.Element; | |
160 case JSONElementType.EndObject: | |
161 case JSONElementType.EndArray: | |
162 return XmlNodeType.EndElement; | |
163 case JSONElementType.Value: | |
164 switch (m_valueContext) { | |
165 case ValueContext.ElementStart: | |
166 case ValueContext.ElementEmpty: | |
167 return XmlNodeType.Element; | |
168 case ValueContext.ElementValue: | |
169 return XmlNodeType.Text; | |
170 case ValueContext.ElementEnd: | |
171 return XmlNodeType.EndElement; | |
172 default: | |
173 throw new InvalidOperationException(); | |
174 } | |
175 default: | |
176 throw new InvalidOperationException(); | |
177 } | |
178 } | |
179 } | |
180 | |
181 public override string Prefix { | |
182 get { return m_prefix; } | |
183 } | |
184 | |
185 public override bool Read() { | |
186 if (m_state != System.Xml.ReadState.Interactive && m_state != System.Xml.ReadState.Initial) | |
187 return false; | |
188 | |
189 if (m_state == ReadState.Initial) | |
190 m_state = System.Xml.ReadState.Interactive; | |
191 | |
192 try { | |
193 switch (m_parser.ElementType) { | |
194 case JSONElementType.Value: | |
195 switch (m_valueContext) { | |
196 case ValueContext.ElementStart: | |
197 SetLocalName(String.Empty); | |
198 m_valueContext = ValueContext.ElementValue; | |
199 return true; | |
200 case ValueContext.ElementValue: | |
201 RestoreLocalName(); | |
202 m_valueContext = ValueContext.ElementEnd; | |
203 return true; | |
204 case ValueContext.ElementEmpty: | |
205 case ValueContext.ElementEnd: | |
206 RestoreLocalName(); | |
207 break; | |
208 } | |
209 break; | |
210 case JSONElementType.EndArray: | |
211 case JSONElementType.EndObject: | |
212 RestoreLocalName(); | |
213 break; | |
214 } | |
60 | 215 string itemName = m_parser.ElementType == JSONElementType.None ? m_rootName : m_flattenArrays ? m_localName.localName : m_arrayItemName; |
58 | 216 while (m_parser.Read()) { |
217 if (!String.IsNullOrEmpty(m_parser.ElementName)) | |
218 itemName = m_parser.ElementName; | |
219 | |
220 switch (m_parser.ElementType) { | |
221 case JSONElementType.BeginArray: | |
222 if (m_flattenArrays && !m_localName.isArray) { | |
223 m_depthCorrection--; | |
224 SetLocalName(itemName, true); | |
225 continue; | |
226 } else { | |
227 SetLocalName(itemName, true); | |
228 } | |
229 break; | |
230 case JSONElementType.BeginObject: | |
231 SetLocalName(itemName); | |
232 break; | |
233 case JSONElementType.EndArray: | |
234 if (m_flattenArrays && !m_localNameStack.Peek().isArray) { | |
235 RestoreLocalName(); | |
236 m_depthCorrection++; | |
237 continue; | |
238 } | |
239 break; | |
240 case JSONElementType.EndObject: | |
241 break; | |
242 case JSONElementType.Value: | |
243 SetLocalName(itemName); | |
244 m_valueContext = m_parser.ElementValue == null ? ValueContext.ElementEmpty : ValueContext.ElementStart; | |
245 break; | |
246 default: | |
247 break; | |
248 } | |
249 return true; | |
250 } | |
251 | |
252 m_state = System.Xml.ReadState.EndOfFile; | |
253 return false; | |
254 } catch { | |
255 m_state = System.Xml.ReadState.Error; | |
256 throw; | |
257 } | |
258 } | |
259 | |
260 public override bool ReadAttributeValue() { | |
261 return false; | |
262 } | |
263 | |
264 public override ReadState ReadState { | |
265 get { return m_state; } | |
266 } | |
267 | |
268 public override void ResolveEntity() { | |
269 // do nothing | |
270 } | |
271 | |
272 public override string Value { | |
63
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
273 get { |
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
274 if (m_parser.ElementValue == null) |
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
275 return String.Empty; |
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
276 if (Convert.GetTypeCode(m_parser.ElementValue) == TypeCode.Double) |
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
277 return ((double)m_parser.ElementValue).ToString(CultureInfo.InvariantCulture); |
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
278 else |
78 | 279 return m_parser.ElementValue.ToString(); |
63
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
280 } |
58 | 281 } |
282 | |
283 void SetLocalName(string name) { | |
284 m_localNameStack.Push(m_localName); | |
285 m_localName.localName = name; | |
286 m_localName.isArray = false; | |
287 } | |
288 | |
289 void SetLocalName(string name, bool isArray) { | |
290 m_localNameStack.Push(m_localName); | |
291 m_localName.localName = name; | |
292 m_localName.isArray = isArray; | |
293 } | |
294 | |
295 void RestoreLocalName() { | |
296 m_localName = m_localNameStack.Pop(); | |
297 } | |
298 | |
299 public override void Close() { | |
63
908b4f340c69
fixed: JSONXmlReader.Value returns invalid textual representation for numbers.
cin
parents:
60
diff
changeset
|
300 |
58 | 301 } |
59 | 302 |
303 protected override void Dispose(bool disposing) { | |
85 | 304 #if MONO |
305 disposing = true; | |
306 #endif | |
59 | 307 if (disposing) { |
64 | 308 m_parser.Dispose(); |
59 | 309 } |
310 base.Dispose(disposing); | |
311 } | |
312 | |
64 | 313 public static JSONXmlReader Create(string file, JSONXmlReaderOptions options) { |
314 return Create(File.OpenText(file), options); | |
315 } | |
60 | 316 |
79 | 317 /// <summary> |
318 /// Creates the XmlReader for the specified text stream with JSON data. | |
319 /// </summary> | |
320 /// <param name="reader">Text reader.</param> | |
321 /// <param name="options">Options.</param> | |
322 /// <remarks> | |
323 /// The reader will be disposed when the XmlReader is disposed. | |
324 /// </remarks> | |
64 | 325 public static JSONXmlReader Create(TextReader reader, JSONXmlReaderOptions options) { |
326 return new JSONXmlReader(new JSONParser(reader, true), options); | |
327 } | |
328 | |
79 | 329 /// <summary> |
330 /// Creates the XmlReader for the specified stream with JSON data. | |
331 /// </summary> | |
332 /// <param name="stream">Stream.</param> | |
333 /// <param name="options">Options.</param> | |
334 /// <remarks> | |
335 /// The stream will be disposed when the XmlReader is disposed. | |
336 /// </remarks> | |
64 | 337 public static JSONXmlReader Create(Stream stream, JSONXmlReaderOptions options) { |
338 Safe.ArgumentNotNull(stream, "stream"); | |
339 // HACK don't dispose StreaReader to keep stream opened | |
340 return Create(new StreamReader(stream), options); | |
60 | 341 } |
58 | 342 } |
343 } |