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