##// END OF EJS Templates
Restored lost JsonXmlCaseTransform from version 2.1
cin -
r265:74e048cbaac8 v3.0.10 v3
parent child
Show More
@@ -0,0 +1,11
1 using System;
2
3 namespace Implab.Xml
4 {
5 public enum JsonXmlCaseTransform
6 {
7 None,
8 UcFirst,
9 LcFirst
10 }
11 } No newline at end of file
@@ -1,191 +1,191
1 1 using Xunit;
2 2 using System;
3 3 using Implab.Automaton;
4 4 using Implab.Xml;
5 5 using System.Xml;
6 6 using Implab.Formats;
7 7 using Implab.Formats.Json;
8 8 using System.IO;
9 9 using Implab.Test.Model;
10 10
11 11 namespace Implab.Test {
12 12 public class JsonTests {
13 13
14 14 [Fact]
15 15 public void TestScannerValidTokens() {
16 16 using (var scanner = JsonStringScanner.Create(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) {
17 17
18 18 Tuple<JsonTokenType, object>[] expexted = {
19 19 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "9123"),
20 20 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
21 21 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-123"),
22 22 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
23 23 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0"),
24 24 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
25 25 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "0.1"),
26 26 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
27 27 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.2"),
28 28 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
29 29 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "-0.1e3"),
30 30 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
31 31 new Tuple<JsonTokenType,object>(JsonTokenType.Number, "1.3E-3"),
32 32 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
33 33 new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"),
34 34 new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, null),
35 35 new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"),
36 36 new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, null),
37 37 new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, null),
38 38 new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, null),
39 39 new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, null),
40 40 new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, null)
41 41 };
42 42
43 43 string value;
44 44 JsonTokenType tokenType;
45 45 for (var i = 0; i < expexted.Length; i++) {
46 46
47 47 Assert.True(scanner.ReadToken(out value, out tokenType));
48 48 Assert.Equal(expexted[i].Item1, tokenType);
49 49 Assert.Equal(expexted[i].Item2, value);
50 50 }
51 51
52 52 Assert.False(scanner.ReadToken(out value, out tokenType));
53 53 }
54 54 }
55 55
56 56 [Fact]
57 57 public void TestScannerBadTokens() {
58 58 var bad = new[] {
59 59 " 1",
60 60 " literal",
61 61 " \"",
62 62 "\"unclosed string",
63 63 "1.bad",
64 64 "001", // should be read as three numbers
65 65 "--10",
66 66 "+10",
67 67 "1.0.0",
68 68 "1e1.0",
69 69 "l1teral0",
70 70 ".123",
71 71 "-.123"
72 72 };
73 73
74 74 foreach (var json in bad) {
75 75 using (var scanner = JsonStringScanner.Create(json)) {
76 76 try {
77 77 string value;
78 78 JsonTokenType token;
79 79 scanner.ReadToken(out value, out token);
80 80 if (!Object.Equals(value, json)) {
81 81 Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value);
82 82 continue;
83 83 }
84 84 Assert.True(false, $"Token '{json}' shouldn't pass");
85 85 } catch (ParserException e) {
86 86 Console.WriteLine(e.Message);
87 87 }
88 88 }
89 89 }
90 90 }
91 91
92 92 [Fact]
93 93 public void JsonXmlReaderSimpleTest() {
94 94 var json = "\"some text\"";
95 95 //Console.WriteLine($"JSON: {json}");
96 96 //Console.WriteLine("XML");
97 97 /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) {
98 98 Assert.AreEqual(xmlReader.ReadState, System.Xml.ReadState.Initial);
99 99
100 100 AssertRead(xmlReader, XmlNodeType.XmlDeclaration);
101 101 AssertRead(xmlReader, XmlNodeType.Element);
102 102 AssertRead(xmlReader, XmlNodeType.Text);
103 103 AssertRead(xmlReader, XmlNodeType.EndElement);
104 104 Assert.IsFalse(xmlReader.Read());
105 105 }*/
106 106
107 107 DumpJsonParse("\"text value\"");
108 108 DumpJsonParse("null");
109 109 DumpJsonParse("true");
110 110 DumpJsonParse("{}");
111 111 DumpJsonParse("[]");
112 112 DumpJsonParse("{\"one\":1, \"two\":2}");
113 113 DumpJsonParse("[1,\"\",2,3]");
114 114 DumpJsonParse("[{\"info\": [7,8,9]}]");
115 115 DumpJsonFlatParse("[1,2,\"\",[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]");
116 116 }
117 117
118 118 [Fact]
119 119 public void XmlToJsonTransform() {
120 120 var person = new Person {
121 121 FirstName = "Charlie",
122 122 LastName = "Brown",
123 123 Age = 19,
124 124 AgeSpecified = true
125 125 };
126 126
127 127 var doc = SerializationHelpers.SerializeAsXmlDocument(person);
128 128
129 129 using (var writer = new StringWriter()) {
130 130 XmlToJson.Default.Transform(doc,null, writer);
131 131 Console.WriteLine(writer.ToString());
132 132 }
133 133 }
134 134
135 135 [Fact]
136 136 public void JsonSerialization() {
137 137 var person = new Person {
138 138 FirstName = "Charlie",
139 139 LastName = "Brown",
140 140 Age = 19,
141 141 AgeSpecified = true,
142 142 Tags = new [] { "brave", "stupid" }
143 143 };
144 144
145 145 var data = SerializationHelpers.SerializeJsonAsString(person);
146 146 Console.WriteLine(data);
147 147 var clone = SerializationHelpers.DeserializeJsonFromString<Person>(data);
148 148
149 149 Assert.Equal(person.FirstName, clone.FirstName);
150 150 Assert.Equal(person.LastName, clone.LastName);
151 151 Assert.Equal(person.Age, clone.Age);
152 152 Assert.Equal(person.AgeSpecified, clone.AgeSpecified);
153 153 Assert.Equal(person.Tags, person.Tags);
154 154 }
155 155
156 156 void AssertRead(XmlReader reader, XmlNodeType expected) {
157 157 Assert.True(reader.Read());
158 158 Console.WriteLine($"{new string(' ', reader.Depth * 2)}{reader}");
159 159 Assert.Equal(expected, reader.NodeType);
160 160 }
161 161
162 162 void DumpJsonParse(string json) {
163 163 Console.WriteLine($"JSON: {json}");
164 164 Console.WriteLine("XML");
165 165 using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings {
166 166 Indent = true,
167 167 CloseOutput = false,
168 168 ConformanceLevel = ConformanceLevel.Document
169 169 }))
170 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) {
170 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "Data", NodesPrefix = "json", CaseTransform = JsonXmlCaseTransform.UcFirst, ArrayItemName = "Item" })) {
171 171 xmlWriter.WriteNode(xmlReader, false);
172 172 }
173 173 Console.WriteLine();
174 174 }
175 175
176 176 void DumpJsonFlatParse(string json) {
177 177 Console.WriteLine($"JSON: {json}");
178 178 Console.WriteLine("XML");
179 179 using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings {
180 180 Indent = true,
181 181 CloseOutput = false,
182 182 ConformanceLevel = ConformanceLevel.Document
183 183 }))
184 184 using (var xmlReader = new JsonXmlReader(JsonReader.ParseString(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) {
185 185 xmlWriter.WriteNode(xmlReader, false);
186 186 }
187 187 Console.WriteLine();
188 188 }
189 189 }
190 190 }
191 191
@@ -1,26 +1,26
1 1 <Project Sdk="Microsoft.NET.Sdk">
2 2
3 3 <PropertyGroup>
4 4 <Authors>Sergey Smirnov</Authors>
5 5 <Title>Implab library</Title>
6 6 <Description>Provides some helper clesses like XML serialization helpers, JSON XML reader,
7 7 JSON pull-parser, ECMA-style promises, lightweight synchonization routines Signal
8 8 and SharedLock, Trace helpers on top of System.Diagnostics, ObjectPool etc.
9 9 </Description>
10 10 <Copyright>2012-2018 Sergey Smirnov</Copyright>
11 <Version>3.0.8</Version>
11 <Version>3.0.10</Version>
12 12 <PackageLicenseUrl>https://hg.implab.org/pub/ImplabNet/file/tip/Implab/license.txt</PackageLicenseUrl>
13 13 <PackageProjectUrl>https://implab.org</PackageProjectUrl>
14 14 <RepositoryUrl>https://hg.implab.org/pub/ImplabNet/</RepositoryUrl>
15 15 <RepositoryType>mercurial</RepositoryType>
16 16 <PackageTags>IMPLAB;Json pull-parser;Json Xml;async;diagnostics;serialization;</PackageTags>
17 17 <TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
18 18 <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride>
19 19 <DefineConstants Condition="'$(TargetFramework)'=='net46'">NETFX_TRACE_BUG;$(DefineConstants)</DefineConstants>
20 20 </PropertyGroup>
21 21
22 22 <ItemGroup>
23 23 <EmbeddedResource Include="Xml\json.xsl"/>
24 24 </ItemGroup>
25 25
26 26 </Project>
@@ -1,636 +1,664
1 1 using Implab.Formats.Json;
2 2 using System;
3 3 using System.Collections.Generic;
4 4 using System.Globalization;
5 5 using System.IO;
6 6 using System.Linq;
7 7 using System.Xml;
8 8
9 9 namespace Implab.Xml {
10 10 public class JsonXmlReader : XmlReader {
11 11 struct JsonContext {
12 12 public string localName;
13 13 public bool skip;
14 14 }
15 15
16 16 JsonReader m_parser;
17 17 JsonXmlReaderOptions m_options;
18 18 JsonXmlReaderPosition m_position = JsonXmlReaderPosition.Initial;
19 19 XmlNameTable m_nameTable;
20 20
21 21 readonly string m_jsonRootName;
22 22 readonly string m_jsonNamespace;
23 23 readonly string m_jsonPrefix;
24 24 readonly bool m_jsonFlattenArrays;
25 25 readonly string m_jsonArrayItemName;
26 26
27 27 string m_jsonLocalName;
28 28 string m_jsonValueName;
29 29 bool m_jsonSkip; // indicates wheather to generate closing tag for objects or arrays
30 30
31 31 readonly Stack<JsonContext> m_jsonNameStack = new Stack<JsonContext>();
32 32
33 33 XmlQualifiedName m_elementQName;
34 34 string m_elementPrefix;
35 35 int m_elementDepth;
36 36 bool m_elementIsEmpty;
37 37
38 38 XmlQualifiedName m_qName;
39 39 string m_prefix;
40 40 int m_xmlDepth;
41 41
42 42 XmlSimpleAttribute[] m_attributes;
43 43 string m_value;
44 44 bool m_isEmpty;
45 45
46 46 XmlNodeType m_nodeType = XmlNodeType.None;
47 47
48 48 bool m_isAttribute; // indicates that we are reading attribute nodes
49 49 int m_currentAttribute;
50 50 bool m_currentAttributeRead;
51 51
52 52
53 53 XmlNameContext m_context;
54 54
55 55 readonly string m_xmlnsPrefix;
56 56 readonly string m_xmlnsNamespace;
57 57 readonly string m_xsiPrefix;
58 58 readonly string m_xsiNamespace;
59 readonly JsonXmlCaseTransform m_caseTransform;
59 60
60 61
61 62 public JsonXmlReader(JsonReader parser, JsonXmlReaderOptions options) {
62 63 Safe.ArgumentNotNull(parser, nameof(parser));
63 64 m_parser = parser;
64 65
65 66 m_options = options ?? new JsonXmlReaderOptions();
66 67
67 68 m_jsonFlattenArrays = m_options.FlattenArrays;
68 69 m_nameTable = m_options.NameTable ?? new NameTable();
69 70
70 71 m_jsonRootName = m_nameTable.Add(string.IsNullOrEmpty(m_options.RootName) ? "data" : m_options.RootName);
71 72 m_jsonArrayItemName = m_nameTable.Add(string.IsNullOrEmpty(m_options.ArrayItemName) ? "item" : m_options.ArrayItemName);
72 73 m_jsonNamespace = m_nameTable.Add(m_options.NamespaceUri ?? string.Empty);
73 74 m_jsonPrefix = m_nameTable.Add(m_options.NodesPrefix ?? string.Empty);
74 75 m_xmlnsPrefix = m_nameTable.Add(XmlNameContext.XmlnsPrefix);
75 76 m_xmlnsNamespace = m_nameTable.Add(XmlNameContext.XmlnsNamespace);
76 77 m_xsiPrefix = m_nameTable.Add(XmlNameContext.XsiPrefix);
77 78 m_xsiNamespace = m_nameTable.Add(XmlNameContext.XsiNamespace);
78 79
80 m_caseTransform = m_options.CaseTransform;
81
79 82 // TODO validate m_jsonRootName, m_jsonArrayItemName
80 83
81 84 m_context = new XmlNameContext(null, 0);
82 85 }
83 86
84 87 public override int AttributeCount {
85 88 get {
86 89 return m_attributes == null ? 0 : m_attributes.Length;
87 90 }
88 91 }
89 92
90 93 public override string BaseURI {
91 94 get {
92 95 return string.Empty;
93 96 }
94 97 }
95 98
96 99 public override int Depth {
97 100 get {
98 101 return m_xmlDepth;
99 102 }
100 103 }
101 104
102 105 public override bool EOF {
103 106 get {
104 107 return m_position == JsonXmlReaderPosition.Eof;
105 108 }
106 109 }
107 110
108 111 public override bool IsEmptyElement {
109 112 get { return m_isEmpty; }
110 113 }
111 114
112 115
113 116 public override string LocalName {
114 117 get {
115 118 return m_qName.Name;
116 119 }
117 120 }
118 121
119 122 public override string NamespaceURI {
120 123 get {
121 124 return m_qName.Namespace;
122 125 }
123 126 }
124 127
125 128 public override XmlNameTable NameTable {
126 129 get {
127 130 return m_nameTable;
128 131 }
129 132 }
130 133
131 134 public override XmlNodeType NodeType {
132 135 get {
133 136 return m_nodeType;
134 137 }
135 138 }
136 139
137 140 public override string Prefix {
138 141 get {
139 142 return m_prefix;
140 143 }
141 144 }
142 145
143 146 public override ReadState ReadState {
144 147 get {
145 148 switch (m_position) {
146 149 case JsonXmlReaderPosition.Initial:
147 150 return ReadState.Initial;
148 151 case JsonXmlReaderPosition.Eof:
149 152 return ReadState.EndOfFile;
150 153 case JsonXmlReaderPosition.Closed:
151 154 return ReadState.Closed;
152 155 case JsonXmlReaderPosition.Error:
153 156 return ReadState.Error;
154 157 default:
155 158 return ReadState.Interactive;
156 159 };
157 160 }
158 161 }
159 162
160 163 public override string Value {
161 164 get {
162 165 return m_value;
163 166 }
164 167 }
165
168
166 169 public override string GetAttribute(int i) {
167 170 Safe.ArgumentInRange(i >= 0 && i < AttributeCount, nameof(i));
168 171 return m_attributes[i].Value;
169 172 }
170 173
171 174 public override string GetAttribute(string name) {
172 175 if (m_attributes == null)
173 176 return null;
174 177 var qName = m_context.Resolve(name);
175 178 var attr = Array.Find(m_attributes, x => x.QName == qName);
176 179 var value = attr?.Value;
177 180 return value == string.Empty ? null : value;
178 181 }
179 182
180 183 public override string GetAttribute(string name, string namespaceURI) {
181 184 if (m_attributes == null)
182 185 return null;
183 186 var qName = new XmlQualifiedName(name, namespaceURI);
184 187 var attr = Array.Find(m_attributes, x => x.QName == qName);
185 188 var value = attr?.Value;
186 189 return value == string.Empty ? null : value;
187 190 }
188 191
189 192 public override string LookupNamespace(string prefix) {
190 193 return m_context.ResolvePrefix(prefix);
191 194 }
192 195
193 196 public override bool MoveToAttribute(string name) {
194 197 if (m_attributes == null || m_attributes.Length == 0)
195 198 return false;
196 199
197 200 var qName = m_context.Resolve(name);
198 201 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
199 202 if (index >= 0) {
200 203 MoveToAttributeImpl(index);
201 204 return true;
202 205 }
203 206 return false;
204 207 }
205 208
206 209 public override bool MoveToAttribute(string name, string ns) {
207 210 if (m_attributes == null || m_attributes.Length == 0)
208 211 return false;
209 212
210 213 var qName = m_context.Resolve(name);
211 214 var index = Array.FindIndex(m_attributes, x => x.QName == qName);
212 215 if (index >= 0) {
213 216 MoveToAttributeImpl(index);
214 217 return true;
215 218 }
216 219 return false;
217 220 }
218 221
219 222 void MoveToAttributeImpl(int i) {
220 223 if (!m_isAttribute) {
221 224 m_elementQName = m_qName;
222 225 m_elementDepth = m_xmlDepth;
223 226 m_elementPrefix = m_prefix;
224 227 m_elementIsEmpty = m_isEmpty;
225 228 m_isAttribute = true;
226 229 }
227
230
228 231 var attr = m_attributes[i];
229 232
230 233
231 234 m_currentAttribute = i;
232 235 m_currentAttributeRead = false;
233 236 m_nodeType = XmlNodeType.Attribute;
234 237
235 238 m_xmlDepth = m_elementDepth + 1;
236 239 m_qName = attr.QName;
237 240 m_value = attr.Value;
238 241 m_prefix = attr.Prefix;
239 242 }
240 243
241 244 public override bool MoveToElement() {
242 245 if (m_isAttribute) {
243 246 m_value = null;
244 247 m_nodeType = XmlNodeType.Element;
245 248 m_xmlDepth = m_elementDepth;
246 249 m_prefix = m_elementPrefix;
247 250 m_qName = m_elementQName;
248 251 m_isEmpty = m_elementIsEmpty;
249 252 m_isAttribute = false;
250 253 return true;
251 254 }
252 255 return false;
253 256 }
254 257
255 258 public override bool MoveToFirstAttribute() {
256 259 if (m_attributes != null && m_attributes.Length > 0) {
257 260 MoveToAttributeImpl(0);
258 261 return true;
259 262 }
260 263 return false;
261 264 }
262 265
263 266 public override bool MoveToNextAttribute() {
264 267 if (m_isAttribute) {
265 268 var next = m_currentAttribute + 1;
266 269 if (next < AttributeCount) {
267 270 MoveToAttributeImpl(next);
268 271 return true;
269 272 }
270 273 return false;
271 274 } else {
272 275 return MoveToFirstAttribute();
273 276 }
274 277
275 278 }
276 279
277 280 public override bool ReadAttributeValue() {
278 281 if (!m_isAttribute || m_currentAttributeRead)
279 282 return false;
280 283
281 284 ValueNode(m_attributes[m_currentAttribute].Value);
282 285 m_currentAttributeRead = true;
283 286 return true;
284 287 }
285 288
286 289 public override void ResolveEntity() {
287 290 /* do nothing */
288 291 }
289 292
290 293 /// <summary>
291 294 /// Determines do we need to increase depth after the current node
292 295 /// </summary>
293 296 /// <returns></returns>
294 297 public bool IsSibling() {
295 298 switch (m_nodeType) {
296 299 case XmlNodeType.None: // start document
297 300 case XmlNodeType.Attribute: // after attribute only it's content can be iterated with ReadAttributeValue method
298 301 return false;
299 302 case XmlNodeType.Element:
300 303 // if the elemnt is empty the next element will be it's sibling
301 304 return m_isEmpty;
302 305 default:
303 306 return true;
304 307 }
305 308 }
306 309
307 310 void ValueNode(string value) {
308 311 if (!IsSibling()) // the node is nested
309 312 m_xmlDepth++;
310 313
311 314 m_qName = XmlQualifiedName.Empty;
312 315 m_nodeType = XmlNodeType.Text;
313 316 m_prefix = string.Empty;
314 317 m_value = value;
315 318 m_isEmpty = false;
316 319 m_attributes = null;
317 320 }
318 321
319 322 void ElementNode(string name, string ns, XmlSimpleAttribute[] attrs, bool empty) {
320 323 if (!IsSibling()) // the node is nested
321 324 m_xmlDepth++;
322 325
323 326 var context = m_context;
324 327 List<XmlSimpleAttribute> definedAttrs = null;
325 328
326 329 // define new namespaces
327 330 if (attrs != null) {
328 331 foreach (var attr in attrs) {
329 332 if (attr.QName.Name == "xmlns") {
330 333 if (context == m_context)
331 context = new XmlNameContext(m_context, m_xmlDepth);
334 context = new XmlNameContext(m_context, m_xmlDepth);
332 335 context.DefinePrefix(attr.Value, string.Empty);
333 336 } else if (attr.Prefix == m_xmlnsPrefix) {
334 337 if (context == m_context)
335 338 context = new XmlNameContext(m_context, m_xmlDepth);
336 339 context.DefinePrefix(attr.Value, attr.QName.Name);
337 340 } else {
338 341 string attrPrefix;
339 342 if (string.IsNullOrEmpty(attr.QName.Namespace))
340 343 continue;
341 344
342 345 // auto-define prefixes
343 346 if (!context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) {
344 347 // new namespace prefix added
345 348 attrPrefix = context.CreateNamespacePrefix(attr.QName.Namespace);
346 349 attr.Prefix = attrPrefix;
347 350
348 351 if (definedAttrs == null)
349 352 definedAttrs = new List<XmlSimpleAttribute>();
350 353
351 354 definedAttrs.Add(new XmlSimpleAttribute(attrPrefix, m_xmlnsNamespace, m_xmlnsPrefix, attr.QName.Namespace));
352 355 }
353 356 }
354 357 }
355 358 }
356 359
357 360 string p;
358 361 // auto-define prefixes
359 362 if (!context.LookupNamespacePrefix(ns, out p)) {
360 363 if (context == m_context)
361 364 context = new XmlNameContext(m_context, m_xmlDepth);
362 365 p = context.CreateNamespacePrefix(ns);
363 366 if (definedAttrs == null)
364 367 definedAttrs = new List<XmlSimpleAttribute>();
365 368
366 369 definedAttrs.Add(new XmlSimpleAttribute(p, m_xmlnsNamespace, m_xmlnsPrefix, ns));
367 370 }
368 371
369 372 if (definedAttrs != null) {
370 373 if (attrs != null)
371 374 definedAttrs.AddRange(attrs);
372 375 attrs = definedAttrs.ToArray();
373 376 }
374 377
375 378 if (!empty)
376 379 m_context = context;
377 380
378 381 m_nodeType = XmlNodeType.Element;
379 382 m_qName = new XmlQualifiedName(name, ns);
380 383 m_prefix = p;
381 384 m_value = null;
382 385 m_isEmpty = empty;
383 386 m_attributes = attrs;
384 387 }
385 388
386 389 void EndElementNode(string name, string ns) {
387 390 if (IsSibling()) {
388 391 // closing the element which has children
389 392 m_xmlDepth--;
390 393 }
391 394
392 395 string p;
393 396 if (!m_context.LookupNamespacePrefix(ns, out p))
394 397 throw new Exception($"Failed to lookup namespace '{ns}'");
395 398
396 399 if (m_context.Depth == m_xmlDepth)
397 400 m_context = m_context.ParentContext;
398 401
399 402 m_nodeType = XmlNodeType.EndElement;
400 403 m_prefix = p;
401 404 m_qName = new XmlQualifiedName(name, ns);
402 405 m_value = null;
403 406 m_attributes = null;
404 407 m_isEmpty = false;
405 408 }
406 409
407 410 void XmlDeclaration() {
408 411 if (!IsSibling()) // the node is nested
409 412 m_xmlDepth++;
410 413 m_nodeType = XmlNodeType.XmlDeclaration;
411 414 m_qName = new XmlQualifiedName("xml");
412 415 m_value = "version='1.0'";
413 416 m_prefix = string.Empty;
414 417 m_attributes = null;
415 418 m_isEmpty = false;
416 419 }
417 420
418 421 public override bool Read() {
419 422 try {
420 423 string elementName;
421 424 XmlSimpleAttribute[] elementAttrs = null;
422 425 MoveToElement();
423 426
424 427 switch (m_position) {
425 428 case JsonXmlReaderPosition.Initial:
426 429 m_jsonLocalName = m_jsonRootName;
427 430 m_jsonSkip = false;
428 431 XmlDeclaration();
429 432 m_position = JsonXmlReaderPosition.Declaration;
430 433 return true;
431 434 case JsonXmlReaderPosition.Declaration:
432 435 elementAttrs = new[] {
433 436 new XmlSimpleAttribute(m_xsiPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_xsiNamespace),
434 437 string.IsNullOrEmpty(m_jsonPrefix) ?
435 438 new XmlSimpleAttribute(m_xmlnsPrefix, string.Empty, string.Empty, m_jsonNamespace) :
436 439 new XmlSimpleAttribute(m_jsonPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_jsonNamespace)
437 440 };
438 441 break;
439 442 case JsonXmlReaderPosition.ValueElement:
440 443 if (!m_isEmpty) {
441 444 if (m_parser.ElementValue != null && !m_parser.ElementValue.Equals(string.Empty))
442 445 ValueNode(m_parser.ElementValue);
443 446 else
444 447 goto case JsonXmlReaderPosition.ValueContent;
445 448 m_position = JsonXmlReaderPosition.ValueContent;
446 449 return true;
447 450 } else {
448 451 m_position = JsonXmlReaderPosition.ValueEndElement;
449 452 break;
450 453 }
451 454 case JsonXmlReaderPosition.ValueContent:
452 455 EndElementNode(m_jsonValueName, m_jsonNamespace);
453 456 m_position = JsonXmlReaderPosition.ValueEndElement;
454 457 return true;
455 458 case JsonXmlReaderPosition.Eof:
456 459 case JsonXmlReaderPosition.Closed:
457 460 case JsonXmlReaderPosition.Error:
458 461 return false;
459 462 }
460 463
461 464 while (m_parser.Read()) {
462 var jsonName = m_nameTable.Add(m_parser.ElementName);
465 var jsonName = m_nameTable.Add(TransformJsonName(m_parser.ElementName));
463 466
464 467 switch (m_parser.ElementType) {
465 468 case JsonElementType.BeginObject:
466 469 if (!EnterJsonObject(jsonName, out elementName))
467 470 continue;
468 471
469 472 m_position = JsonXmlReaderPosition.BeginObject;
470 473 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
471 474 break;
472 475 case JsonElementType.EndObject:
473 476 if (!LeaveJsonScope(out elementName))
474 477 continue;
475 478
476 479 m_position = JsonXmlReaderPosition.EndObject;
477 480 EndElementNode(elementName, m_jsonNamespace);
478 481 break;
479 482 case JsonElementType.BeginArray:
480 483 if (!EnterJsonArray(jsonName, out elementName))
481 484 continue;
482 485
483 486 m_position = JsonXmlReaderPosition.BeginArray;
484 487 ElementNode(elementName, m_jsonNamespace, elementAttrs, false);
485 488 break;
486 489 case JsonElementType.EndArray:
487 490 if (!LeaveJsonScope(out elementName))
488 491 continue;
489 492
490 493 m_position = JsonXmlReaderPosition.EndArray;
491 494 EndElementNode(elementName, m_jsonNamespace);
492 495 break;
493 496 case JsonElementType.Value:
494 497 if (!VisitJsonValue(jsonName, out m_jsonValueName))
495 498 continue;
496 499
497 500 m_position = JsonXmlReaderPosition.ValueElement;
498 501 if (m_parser.ElementValue == null)
499 502 // generate empty element with xsi:nil="true" attribute
500 503 ElementNode(
501 504 m_jsonValueName,
502 505 m_jsonNamespace,
503 506 new[] {
504 507 new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, "true")
505 508 },
506 509 true
507 510 );
508 511 else
509 512 ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue.Equals(string.Empty));
510 513 break;
511 514 default:
512 515 throw new Exception($"Unexpected JSON element {m_parser.ElementType}: {m_parser.ElementName}");
513 516 }
514 517 return true;
515 518 }
516 519
517 520 m_position = JsonXmlReaderPosition.Eof;
518 521 return false;
519 522 } catch {
520 523 m_position = JsonXmlReaderPosition.Error;
521 524 throw;
522 525 }
523 526 }
524 527
525 528 void SaveJsonName() {
526 529 m_jsonNameStack.Push(new JsonContext {
527 530 skip = m_jsonSkip,
528 531 localName = m_jsonLocalName
529 532 });
530 533
531 534 }
532 535
533 536 bool EnterJsonObject(string name, out string elementName) {
534 537 SaveJsonName();
535 538 m_jsonSkip = false;
536 539
537 540 if (string.IsNullOrEmpty(name)) {
538 541 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
539 542 m_jsonLocalName = m_jsonArrayItemName;
540 543 } else {
541 544 m_jsonLocalName = name;
542 545 }
543 546
544 547 elementName = m_jsonLocalName;
545 548 return true;
546 549 }
547 550
548 551 /// <summary>
549 552 /// Called when JSON parser visits BeginArray ('[') element.
550 553 /// </summary>
551 554 /// <param name="name">Optional property name if the array is the member of an object</param>
552 555 /// <returns>true if element should be emited, false otherwise</returns>
553 556 bool EnterJsonArray(string name, out string elementName) {
554 557 SaveJsonName();
555 558
556 559 if (string.IsNullOrEmpty(name)) {
557 560 // m_jsonNameStack.Count == 1 means the root node
558 561 if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays)
559 562 m_jsonLocalName = m_jsonArrayItemName;
560 563
561 564 m_jsonSkip = false; // we should not flatten arrays inside arrays or in the document root
562 565 } else {
563 566 m_jsonLocalName = name;
564 567 m_jsonSkip = m_jsonFlattenArrays;
565 568 }
566 569 elementName = m_jsonLocalName;
567 570
568 571 return !m_jsonSkip;
569 572 }
570 573
571 574 bool VisitJsonValue(string name, out string elementName) {
572 575 if (string.IsNullOrEmpty(name)) {
573 576 // m_jsonNameStack.Count == 0 means that JSON document consists from simple value
574 577 elementName = (m_jsonNameStack.Count == 0 || m_jsonFlattenArrays) ? m_jsonLocalName : m_jsonArrayItemName;
575 578 } else {
576 579 elementName = name;
577 580 }
578 581 return true;
579 582 }
580 583
581 584 bool LeaveJsonScope(out string elementName) {
582 585 elementName = m_jsonLocalName;
583 586 var skip = m_jsonSkip;
584 587
585 588 var prev = m_jsonNameStack.Pop();
586 589 m_jsonLocalName = prev.localName;
587 590 m_jsonSkip = prev.skip;
588 591
589 592 return !skip;
590 593 }
591 594
595 private string TransformJsonName(string name) {
596 if (m_caseTransform == JsonXmlCaseTransform.None || string.IsNullOrEmpty(name)) {
597 return name;
598 } else if (m_caseTransform == JsonXmlCaseTransform.UcFirst) {
599 return JsonXmlReader.UppercaseFirst(name);
600 } else {
601 return JsonXmlReader.LowercaseFirst(name);
602 }
603 }
604
592 605 protected override void Dispose(bool disposing) {
593 606 if (disposing)
594 607 Safe.Dispose(m_parser);
595 608 base.Dispose(true);
596 609 }
597 610
598 611 public override string ToString() {
599 612 switch (NodeType) {
600 613 case XmlNodeType.Element:
601 614 return $"<{Name} {string.Join(" ", (m_attributes ?? new XmlSimpleAttribute[0]).Select(x => $"{x.Prefix}{(string.IsNullOrEmpty(x.Prefix) ? "" : ":")}{x.QName.Name}='{x.Value}'"))} {(IsEmptyElement ? "/" : "")}>";
602 615 case XmlNodeType.Attribute:
603 616 return $"@{Name}";
604 617 case XmlNodeType.Text:
605 618 return $"{Value}";
606 619 case XmlNodeType.CDATA:
607 620 return $"<![CDATA[{Value}]]>";
608 621 case XmlNodeType.EntityReference:
609 622 return $"&{Name};";
610 623 case XmlNodeType.EndElement:
611 624 return $"</{Name}>";
612 625 default:
613 626 return $".{NodeType} {Name} {Value}";
614 627 }
615 628 }
616 629
617 630 #region static methods
618 631
632 //
633 // Static Methods
634 //
635 private static string LowercaseFirst(string s) {
636 char[] array = s.ToCharArray();
637 array[0] = char.ToLower(array[0]);
638 return new string(array);
639 }
640
641 private static string UppercaseFirst(string s) {
642 char[] array = s.ToCharArray();
643 array[0] = char.ToUpper(array[0]);
644 return new string(array);
645 }
646
619 647 public static JsonXmlReader CreateJsonXmlReader(TextReader textReader, JsonXmlReaderOptions options = null) {
620 648 var jsonReader = JsonReader.Create(textReader);
621 649 return new JsonXmlReader(jsonReader, options);
622 650 }
623 651
624 652 public static JsonXmlReader CreateJsonXmlReader(Stream stream, JsonXmlReaderOptions options = null) {
625 653 var jsonReader = JsonReader.Create(stream);
626 654 return new JsonXmlReader(jsonReader, options);
627 655 }
628 656
629 657 public static JsonXmlReader CreateJsonXmlReader(string file, JsonXmlReaderOptions options = null) {
630 658 var jsonReader = JsonReader.Create(file);
631 659 return new JsonXmlReader(jsonReader, options);
632 660 }
633 661
634 662 #endregion
635 663 }
636 664 }
@@ -1,66 +1,68
1 1
2 2 using System;
3 3 using System.Xml;
4 4
5 5 namespace Implab.Xml {
6 6 /// <summary>
7 7 /// Набор необязательных параметров для <see cref="JsonXmlReader"/>, позволяющий управлять процессом
8 8 /// интерпретации <c>JSON</c> документа.
9 9 /// </summary>
10 10 public class JsonXmlReaderOptions : ICloneable {
11 11 /// <summary>
12 12 /// Пространство имен в котором будут располагаться читаемые элементы документа
13 13 /// </summary>
14 14 public string NamespaceUri {
15 15 get;
16 16 set;
17 17 }
18 18
19 19 /// <summary>
20 20 /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив
21 21 /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов
22 22 /// определяются свойством <see cref="ArrayItemName"/>. По умолчанию <c>false</c>.
23 23 /// </summary>
24 24 public bool FlattenArrays {
25 25 get;
26 26 set;
27 27 }
28 28
29 29 /// <summary>
30 30 /// Префикс, для узлов документа
31 31 /// </summary>
32 32 public string NodesPrefix {
33 33 get;
34 34 set;
35 35 }
36 36
37 37 /// <summary>
38 38 /// Имя корневого элемента в xml документе
39 39 /// </summary>
40 40 public string RootName {
41 41 get;
42 42 set;
43 43 }
44 44
45 45 /// <summary>
46 46 /// Имя элемента для массивов, если не включена опция <see cref="FlattenArrays"/>.
47 47 /// По умолчанию <c>item</c>.
48 48 /// </summary>
49 49 public string ArrayItemName {
50 50 get;
51 51 set;
52 52 }
53 53
54 54 /// <summary>
55 55 /// Таблица атомизированных строк для построения документа.
56 56 /// </summary>
57 57 public XmlNameTable NameTable {
58 58 get;
59 59 set;
60 60 }
61 61
62 public JsonXmlCaseTransform CaseTransform { get; set; }
63
62 64 public object Clone() {
63 65 return MemberwiseClone();
64 66 }
65 67 }
66 68 }
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now