Mercurial > pub > ImplabNet
changeset 227:8d5de4eb9c2c v2
Reimplemented JsonXmlReader, added support for null values: JSON null values are
mapped to empty nodes with 'xsi:nil' attribute set to 'true'
author | cin |
---|---|
date | Sat, 09 Sep 2017 03:53:13 +0300 |
parents | 9428ea36838e |
children | 6fa235c5a760 |
files | .hgignore Implab.Format.Test/Implab.Format.Test.csproj Implab.Format.Test/JsonTests.cs Implab.Format.Test/packages.config Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj Implab.Test/Implab.Format.Test/JsonTests.cs Implab.Test/Implab.Format.Test/packages.config Implab.Test/Implab.Test.csproj Implab.sln Implab/Formats/JSON/JSONParser.cs Implab/Formats/JSON/JSONXmlReaderOptions.cs Implab/Implab.csproj Implab/Parallels/AsyncQueue.cs Implab/Xml/JsonXmlReader.cs Implab/Xml/JsonXmlReaderOptions.cs Implab/Xml/JsonXmlReaderPosition.cs Implab/Xml/XmlNameContext.cs Implab/Xml/XmlSimpleAttribute.cs |
diffstat | 18 files changed, 1107 insertions(+), 209 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri Aug 25 02:16:35 2017 +0300 +++ b/.hgignore Sat Sep 09 03:53:13 2017 +0300 @@ -18,3 +18,6 @@ Implab.Test/Implab.Format.Test/bin/ Implab.Test/Implab.Format.Test/obj/ *.suo +Implab.Format.Test/bin/ +Implab.Format.Test/obj/ +packages/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.Format.Test/Implab.Format.Test.csproj Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>8.0.30703</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>Implab.Format.Test</RootNamespace> + <AssemblyName>Implab.Format.Test</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <ReleaseVersion>0.2</ReleaseVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG;</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>full</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL"> + <HintPath>..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath> + <Private>True</Private> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="JsonTests.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <ItemGroup> + <ProjectReference Include="..\Implab\Implab.csproj"> + <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project> + <Name>Implab</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> +</Project> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.Format.Test/JsonTests.cs Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,145 @@ +using NUnit.Framework; +using System; +using Implab.Formats.JSON; +using Implab.Automaton; +using Implab.Xml; +using System.Xml; +using System.Text; + +namespace Implab.Format.Test { + [TestFixture] + public class JsonTests { + [Test] + public void TestScannerValidTokens() { + using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) { + + Tuple<JsonTokenType, object>[] expexted = { + new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"), + new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), + new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"), + new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, " ["), + new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, "]"), + new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, "{"), + new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, "}"), + new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, ":") + }; + + object value; + JsonTokenType tokenType; + for (var i = 0; i < expexted.Length; i++) { + + Assert.IsTrue(scanner.ReadToken(out value, out tokenType)); + Assert.AreEqual(expexted[i].Item1, tokenType); + Assert.AreEqual(expexted[i].Item2, value); + } + + Assert.IsFalse(scanner.ReadToken(out value, out tokenType)); + } + } + + [Test] + public void TestScannerBadTokens() { + var bad = new[] { + " 1", + " literal", + " \"", + "\"unclosed string", + "1.bad", + "001", // should be read as three numbers + "--10", + "+10", + "1.0.0", + "1e1.0", + "l1teral0", + ".123", + "-.123" + }; + + foreach (var json in bad) { + using (var scanner = new JSONScanner(json)) { + try { + object value; + JsonTokenType token; + scanner.ReadToken(out value, out token); + if (!Object.Equals(value, json)) { + Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value); + continue; + } + Assert.Fail("Token '{0}' shouldn't pass", json); + } catch (ParserException e) { + Console.WriteLine(e.Message); + } + } + } + } + + [Test] + public void JsonXmlReaderSimpleTest() { + var json = "\"some text\""; + //Console.WriteLine($"JSON: {json}"); + //Console.WriteLine("XML"); + /*using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", RootName = "string", NodesPrefix = "json" })) { + Assert.AreEqual(xmlReader.ReadState, System.Xml.ReadState.Initial); + + AssertRead(xmlReader, XmlNodeType.XmlDeclaration); + AssertRead(xmlReader, XmlNodeType.Element); + AssertRead(xmlReader, XmlNodeType.Text); + AssertRead(xmlReader, XmlNodeType.EndElement); + Assert.IsFalse(xmlReader.Read()); + }*/ + + //DumpJsonParse("\"text value\""); + //DumpJsonParse("null"); + //DumpJsonParse("true"); + //DumpJsonParse("{}"); + //DumpJsonParse("[]"); + DumpJsonParse("{\"one\":1, \"two\":2}"); + DumpJsonParse("[1,2,3]"); + DumpJsonParse("[{\"info\": [7,8,9]}]"); + DumpJsonFlatParse("[1,2,[3,4],{\"info\": [5,6]},{\"num\": [7,8,null]}, null,[null]]"); + } + + void AssertRead(XmlReader reader, XmlNodeType expected) { + Assert.IsTrue(reader.Read()); + Console.WriteLine($"{new string(' ', reader.Depth*2)}{reader}"); + Assert.AreEqual(expected, reader.NodeType); + } + + void DumpJsonParse(string json) { + Console.WriteLine($"JSON: {json}"); + Console.WriteLine("XML"); + using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "json" })) { + while (xmlReader.Read()) + Console.WriteLine($"{new string(' ', xmlReader.Depth * 2)}{xmlReader}"); + } + } + + void DumpJsonFlatParse(string json) { + Console.WriteLine($"JSON: {json}"); + Console.WriteLine("XML"); + using (var xmlWriter = XmlWriter.Create(Console.Out, new XmlWriterSettings { + Indent = true, + CloseOutput = false, + ConformanceLevel = ConformanceLevel.Document + })) + using (var xmlReader = new JsonXmlReader(new JSONParser(json), new JsonXmlReaderOptions { NamespaceUri = "JsonXmlReaderSimpleTest", NodesPrefix = "", FlattenArrays = true })) { + xmlWriter.WriteNode(xmlReader, false); + } + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.Format.Test/packages.config Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="NUnit" version="3.8.1" targetFramework="net45" /> +</packages> \ No newline at end of file
--- a/Implab.Test/Implab.Format.Test/Implab.Format.Test.csproj Fri Aug 25 02:16:35 2017 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProductVersion>8.0.30703</ProductVersion> - <SchemaVersion>2.0</SchemaVersion> - <ProjectGuid>{4D364996-7ECD-4193-8F90-F223FFEA49DA}</ProjectGuid> - <OutputType>Library</OutputType> - <RootNamespace>Implab.Format.Test</RootNamespace> - <AssemblyName>Implab.Format.Test</AssemblyName> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - <ReleaseVersion>0.2</ReleaseVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug</OutputPath> - <DefineConstants>DEBUG;</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>full</DebugType> - <Optimize>true</Optimize> - <OutputPath>bin\Release</OutputPath> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> - </PropertyGroup> - <ItemGroup> - <Reference Include="System" /> - <Reference Include="nunit.framework"> - <HintPath>..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath> - </Reference> - </ItemGroup> - <ItemGroup> - <Compile Include="JsonTests.cs" /> - </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <ItemGroup> - <ProjectReference Include="..\..\Implab\Implab.csproj"> - <Project>{F550F1F8-8746-4AD0-9614-855F4C4B7F05}</Project> - <Name>Implab</Name> - </ProjectReference> - </ItemGroup> - <ItemGroup> - <None Include="packages.config" /> - </ItemGroup> - <ItemGroup> - <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> - </ItemGroup> -</Project> \ No newline at end of file
--- a/Implab.Test/Implab.Format.Test/JsonTests.cs Fri Aug 25 02:16:35 2017 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -using NUnit.Framework; -using System; -using Implab.Formats.JSON; -using Implab.Automaton; - -namespace Implab.Format.Test { - [TestFixture] - public class JsonTests { - [Test] - public void TestScannerValidTokens() { - using (var scanner = new JSONScanner(@"9123, -123, 0, 0.1, -0.2, -0.1e3, 1.3E-3, ""some \t\n\u0020 text"", literal []{}:")) { - - Tuple<JsonTokenType,object>[] expexted = { - new Tuple<JsonTokenType,object>(JsonTokenType.Number, 9123d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Number, -123d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Number, 0.1d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.2d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Number, -0.1e3d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Number, 1.3E-3d), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.String, "some \t\n text"), - new Tuple<JsonTokenType,object>(JsonTokenType.ValueSeparator, ", "), - new Tuple<JsonTokenType,object>(JsonTokenType.Literal, "literal"), - new Tuple<JsonTokenType,object>(JsonTokenType.BeginArray, " ["), - new Tuple<JsonTokenType,object>(JsonTokenType.EndArray, "]"), - new Tuple<JsonTokenType,object>(JsonTokenType.BeginObject, "{"), - new Tuple<JsonTokenType,object>(JsonTokenType.EndObject, "}"), - new Tuple<JsonTokenType,object>(JsonTokenType.NameSeparator, ":") - }; - - object value; - JsonTokenType tokenType; - for (var i = 0; i < expexted.Length; i++) { - - Assert.IsTrue(scanner.ReadToken(out value, out tokenType)); - Assert.AreEqual(expexted[i].Item1, tokenType); - Assert.AreEqual(expexted[i].Item2, value); - } - - Assert.IsFalse(scanner.ReadToken(out value, out tokenType)); - } - } - - [Test] - public void TestScannerBadTokens() { - var bad = new [] { - " 1", - " literal", - " \"", - "\"unclosed string", - "1.bad", - "001", // should be read as three numbers - "--10", - "+10", - "1.0.0", - "1e1.0", - "l1teral0", - ".123", - "-.123" - }; - - foreach (var json in bad) - using (var scanner = new JSONScanner(json)) { - try { - object value; - JsonTokenType token; - scanner.ReadToken(out value, out token); - if (!Object.Equals(value,json)) { - Console.WriteLine("'{0}' is read as {1}", json, value is String ? String.Format("'{0}'", value) : value ); - continue; - } - Assert.Fail("Token '{0}' shouldn't pass", json); - } catch (ParserException e) { - Console.WriteLine(e.Message); - } - } - - } - } -} -
--- a/Implab.Test/Implab.Format.Test/packages.config Fri Aug 25 02:16:35 2017 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="NUnit" version="2.6.4" targetFramework="net45" /> -</packages> \ No newline at end of file
--- a/Implab.Test/Implab.Test.csproj Fri Aug 25 02:16:35 2017 +0300 +++ b/Implab.Test/Implab.Test.csproj Sat Sep 09 03:53:13 2017 +0300 @@ -76,9 +76,7 @@ <Name>Implab</Name> </ProjectReference> </ItemGroup> - <ItemGroup> - <Folder Include="Implab.Format.Test\" /> - </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets.
--- a/Implab.sln Fri Aug 25 02:16:35 2017 +0300 +++ b/Implab.sln Sat Sep 09 03:53:13 2017 +0300 @@ -1,6 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab", "Implab\Implab.csproj", "{F550F1F8-8746-4AD0-9614-855F4C4B7F05}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE8D8D18-437A-445C-B662-4C2CE79A76F6}" @@ -16,14 +18,32 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Fx.Test", "Implab.Fx.Test\Implab.Fx.Test.csproj", "{2F31E405-E267-4195-A05D-574093C21209}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Implab.Format.Test", "Implab.Format.Test\Implab.Format.Test.csproj", "{4D364996-7ECD-4193-8F90-F223FFEA49DA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug 4.5|Any CPU = Debug 4.5|Any CPU Debug|Any CPU = Debug|Any CPU + Release 4.5|Any CPU = Release 4.5|Any CPU Release|Any CPU = Release|Any CPU - Debug 4.5|Any CPU = Debug 4.5|Any CPU - Release 4.5|Any CPU = Release 4.5|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU {06E706F8-6881-43EB-927E-FFC503AF6ABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -40,24 +60,17 @@ {2F31E405-E267-4195-A05D-574093C21209}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F31E405-E267-4195-A05D-574093C21209}.Release|Any CPU.Build.0 = Release|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {63F92C0C-61BF-48C0-A377-8D67C3C661D0}.Release|Any CPU.Build.0 = Release|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.ActiveCfg = Debug 4.5|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug 4.5|Any CPU.Build.0 = Debug 4.5|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.ActiveCfg = Release 4.5|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release 4.5|Any CPU.Build.0 = Release 4.5|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F550F1F8-8746-4AD0-9614-855F4C4B7F05}.Release|Any CPU.Build.0 = Release|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.ActiveCfg = Debug|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug 4.5|Any CPU.Build.0 = Debug|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.ActiveCfg = Release|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release 4.5|Any CPU.Build.0 = Release|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D364996-7ECD-4193-8F90-F223FFEA49DA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection - GlobalSection(NestedProjects) = preSolution + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = Implab\Implab.csproj @@ -84,7 +97,7 @@ $1.inheritsSet = Mono $1.inheritsScope = text/x-csharp $1.scope = text/x-csharp - $0.TextStylePolicy = $2 + $0.TextStylePolicy = $6 $2.FileWidth = 120 $2.EolMarker = Unix $2.inheritsSet = VisualStudio @@ -93,7 +106,6 @@ $0.DotNetNamingPolicy = $3 $3.DirectoryNamespaceAssociation = PrefixedHierarchical $3.ResourceNamePolicy = MSBuild - $0.TextStylePolicy = $4 $4.FileWidth = 120 $4.TabsToSpaces = False $4.inheritsSet = VisualStudio @@ -103,7 +115,6 @@ $5.inheritsSet = Mono $5.inheritsScope = application/xml $5.scope = application/xml - $0.TextStylePolicy = $6 $6.FileWidth = 120 $6.TabsToSpaces = False $6.inheritsSet = VisualStudio @@ -111,21 +122,19 @@ $6.scope = text/plain $0.NameConventionPolicy = $7 $7.Rules = $8 - $8.NamingRule = $9 + $8.NamingRule = $34 $9.Name = Namespaces $9.AffectedEntity = Namespace $9.VisibilityMask = VisibilityMask $9.NamingStyle = PascalCase $9.IncludeInstanceMembers = True $9.IncludeStaticEntities = True - $8.NamingRule = $10 $10.Name = Types $10.AffectedEntity = Class, Struct, Enum, Delegate $10.VisibilityMask = VisibilityMask $10.NamingStyle = PascalCase $10.IncludeInstanceMembers = True $10.IncludeStaticEntities = True - $8.NamingRule = $11 $11.Name = Interfaces $11.RequiredPrefixes = $12 $12.String = I @@ -134,7 +143,6 @@ $11.NamingStyle = PascalCase $11.IncludeInstanceMembers = True $11.IncludeStaticEntities = True - $8.NamingRule = $13 $13.Name = Attributes $13.RequiredSuffixes = $14 $14.String = Attribute @@ -143,7 +151,6 @@ $13.NamingStyle = PascalCase $13.IncludeInstanceMembers = True $13.IncludeStaticEntities = True - $8.NamingRule = $15 $15.Name = Event Arguments $15.RequiredSuffixes = $16 $16.String = EventArgs @@ -152,7 +159,6 @@ $15.NamingStyle = PascalCase $15.IncludeInstanceMembers = True $15.IncludeStaticEntities = True - $8.NamingRule = $17 $17.Name = Exceptions $17.RequiredSuffixes = $18 $18.String = Exception @@ -161,35 +167,30 @@ $17.NamingStyle = PascalCase $17.IncludeInstanceMembers = True $17.IncludeStaticEntities = True - $8.NamingRule = $19 $19.Name = Methods $19.AffectedEntity = Methods $19.VisibilityMask = VisibilityMask $19.NamingStyle = PascalCase $19.IncludeInstanceMembers = True $19.IncludeStaticEntities = True - $8.NamingRule = $20 $20.Name = Static Readonly Fields $20.AffectedEntity = ReadonlyField $20.VisibilityMask = Internal, Protected, Public $20.NamingStyle = CamelCase $20.IncludeInstanceMembers = False $20.IncludeStaticEntities = True - $8.NamingRule = $21 $21.Name = Fields (Non Private) $21.AffectedEntity = Field $21.VisibilityMask = Internal, Public $21.NamingStyle = CamelCase $21.IncludeInstanceMembers = True $21.IncludeStaticEntities = True - $8.NamingRule = $22 $22.Name = ReadOnly Fields (Non Private) $22.AffectedEntity = ReadonlyField $22.VisibilityMask = Internal, Public $22.NamingStyle = CamelCase $22.IncludeInstanceMembers = True $22.IncludeStaticEntities = False - $8.NamingRule = $23 $23.Name = Fields (Private) $23.RequiredPrefixes = $24 $24.String = m_ @@ -198,7 +199,6 @@ $23.NamingStyle = CamelCase $23.IncludeInstanceMembers = True $23.IncludeStaticEntities = False - $8.NamingRule = $25 $25.Name = Static Fields (Private) $25.RequiredPrefixes = $26 $26.String = _ @@ -207,7 +207,6 @@ $25.NamingStyle = CamelCase $25.IncludeInstanceMembers = False $25.IncludeStaticEntities = True - $8.NamingRule = $27 $27.Name = ReadOnly Fields (Private) $27.RequiredPrefixes = $28 $28.String = m_ @@ -216,42 +215,36 @@ $27.NamingStyle = CamelCase $27.IncludeInstanceMembers = True $27.IncludeStaticEntities = False - $8.NamingRule = $29 $29.Name = Constant Fields $29.AffectedEntity = ConstantField $29.VisibilityMask = VisibilityMask $29.NamingStyle = AllUpper $29.IncludeInstanceMembers = True $29.IncludeStaticEntities = True - $8.NamingRule = $30 $30.Name = Properties $30.AffectedEntity = Property $30.VisibilityMask = VisibilityMask $30.NamingStyle = PascalCase $30.IncludeInstanceMembers = True $30.IncludeStaticEntities = True - $8.NamingRule = $31 $31.Name = Events $31.AffectedEntity = Event $31.VisibilityMask = VisibilityMask $31.NamingStyle = PascalCase $31.IncludeInstanceMembers = True $31.IncludeStaticEntities = True - $8.NamingRule = $32 $32.Name = Enum Members $32.AffectedEntity = EnumMember $32.VisibilityMask = VisibilityMask $32.NamingStyle = PascalCase $32.IncludeInstanceMembers = True $32.IncludeStaticEntities = True - $8.NamingRule = $33 $33.Name = Parameters $33.AffectedEntity = Parameter, LocalVariable $33.VisibilityMask = VisibilityMask $33.NamingStyle = CamelCase $33.IncludeInstanceMembers = True $33.IncludeStaticEntities = True - $8.NamingRule = $34 $34.Name = Type Parameters $34.RequiredPrefixes = $35 $35.String = T @@ -264,7 +257,4 @@ GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = Implab.vsmdi EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection EndGlobal
--- a/Implab/Formats/JSON/JSONParser.cs Fri Aug 25 02:16:35 2017 +0300 +++ b/Implab/Formats/JSON/JSONParser.cs Sat Sep 09 03:53:13 2017 +0300 @@ -110,7 +110,8 @@ #endregion readonly JSONScanner m_scanner; - MemberContext m_memberContext; + // json starts from the value context and may content even a single literal + MemberContext m_memberContext = MemberContext.MemberValue; JSONElementType m_elementType; object m_elementValue;
--- a/Implab/Formats/JSON/JSONXmlReaderOptions.cs Fri Aug 25 02:16:35 2017 +0300 +++ b/Implab/Formats/JSON/JSONXmlReaderOptions.cs Sat Sep 09 03:53:13 2017 +0300 @@ -1,16 +1,16 @@ using System.Xml; -namespace Implab.Formats.JSON { +namespace Implab.Xml { /// <summary> /// Набор необязательных параметров для <see cref="JSONXmlReader"/>, позволяющий управлять процессом /// интерпретации <c>JSON</c> документа. /// </summary> - public class JSONXmlReaderOptions { + public class JsonXmlReaderOptions { /// <summary> /// Пространство имен в котором будут располагаться читаемые элементы документа /// </summary> - public string NamespaceURI { + public string NamespaceUri { get; set; }
--- a/Implab/Implab.csproj Fri Aug 25 02:16:35 2017 +0300 +++ b/Implab/Implab.csproj Sat Sep 09 03:53:13 2017 +0300 @@ -198,6 +198,11 @@ <Compile Include="FailedPromise.cs" /> <Compile Include="FailedPromiseT.cs" /> <Compile Include="Components\PollingComponent.cs" /> + <Compile Include="Xml\JsonXmlReader.cs" /> + <Compile Include="Xml\JsonXmlReaderOptions.cs" /> + <Compile Include="Xml\JsonXmlReaderPosition.cs" /> + <Compile Include="Xml\XmlSimpleAttribute.cs" /> + <Compile Include="Xml\XmlNameContext.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup /> @@ -210,7 +215,7 @@ <DotNetNamingPolicy DirectoryNamespaceAssociation="PrefixedHierarchical" ResourceNamePolicy="MSBuild" /> <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="application/xml" /> <XmlFormattingPolicy scope="application/xml"> - <DefaultFormat OmitXmlDeclaration="False" NewLineChars="
" IndentContent="True" ContentIndentString="	" AttributesInNewLine="False" MaxAttributesPerLine="10" AttributesIndentString="	" WrapAttributes="False" AlignAttributes="False" AlignAttributeValues="False" QuoteChar=""" SpacesBeforeAssignment="0" SpacesAfterAssignment="0" EmptyLinesBeforeStart="0" EmptyLinesAfterStart="0" EmptyLinesBeforeEnd="0" EmptyLinesAfterEnd="0" /> + <DefaultFormat OmitXmlDeclaration="False" NewLineChars="
" IndentContent="True" ContentIndentString=" " AttributesInNewLine="False" MaxAttributesPerLine="10" AttributesIndentString=" " WrapAttributes="False" AlignAttributes="False" AlignAttributeValues="False" QuoteChar=""" SpacesBeforeAssignment="0" SpacesAfterAssignment="0" EmptyLinesBeforeStart="0" EmptyLinesAfterStart="0" EmptyLinesBeforeEnd="0" EmptyLinesAfterEnd="0" /> </XmlFormattingPolicy> <TextStylePolicy FileWidth="120" TabWidth="4" TabsToSpaces="False" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" scope="text/plain" /> <NameConventionPolicy>
--- a/Implab/Parallels/AsyncQueue.cs Fri Aug 25 02:16:35 2017 +0300 +++ b/Implab/Parallels/AsyncQueue.cs Sat Sep 09 03:53:13 2017 +0300 @@ -385,8 +385,6 @@ if (next == null) { if (first != Interlocked.CompareExchange(ref m_last, null, first)) { - /*while (first.next == null) - Thread.MemoryBarrier();*/ // race // someone already updated the tail, restore the pointer to the queue head @@ -394,12 +392,6 @@ } // the tail is updated } - - // we need to update the head - //Interlocked.CompareExchange(ref m_first, next, first); - // if the head is already updated then give up - //return; - } public void Clear() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/JsonXmlReader.cs Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,629 @@ +using Implab.Formats.JSON; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Implab.Xml { + public class JsonXmlReader : XmlReader { + struct JsonContext { + public string localName; + public bool skip; + } + + JSONParser m_parser; + JsonXmlReaderOptions m_options; + JsonXmlReaderPosition m_position = JsonXmlReaderPosition.Initial; + XmlNameTable m_nameTable; + + readonly string m_jsonRootName; + readonly string m_jsonNamespace; + readonly string m_jsonPrefix; + readonly bool m_jsonFlattenArrays; + readonly string m_jsonArrayItemName; + + string m_jsonLocalName; + string m_jsonValueName; + bool m_jsonSkip; // indicates wheather to generate closing tag for objects or arrays + + readonly Stack<JsonContext> m_jsonNameStack = new Stack<JsonContext>(); + + XmlQualifiedName m_elementQName; + string m_elementPrefix; + int m_elementDepth; + bool m_elementIsEmpty; + + XmlQualifiedName m_qName; + string m_prefix; + int m_xmlDepth; + + XmlSimpleAttribute[] m_attributes; + object m_value; + bool m_isEmpty; + + XmlNodeType m_nodeType = XmlNodeType.None; + + bool m_isAttribute; // indicates that we are reading attribute nodes + int m_currentAttribute; + bool m_currentAttributeRead; + + + XmlNameContext m_context; + int m_nextPrefix = 1; + + readonly string m_xmlnsPrefix; + readonly string m_xmlnsNamespace; + readonly string m_xsiPrefix; + readonly string m_xsiNamespace; + + + public JsonXmlReader(JSONParser parser, JsonXmlReaderOptions options) { + Safe.ArgumentNotNull(parser, nameof(parser)); + m_parser = parser; + + m_options = options ?? new JsonXmlReaderOptions(); + + m_jsonFlattenArrays = m_options.FlattenArrays; + m_nameTable = m_options.NameTable ?? new NameTable(); + + m_jsonRootName = m_nameTable.Add(string.IsNullOrEmpty(m_options.RootName) ? "data" : m_options.RootName); + m_jsonArrayItemName = m_nameTable.Add(string.IsNullOrEmpty(m_options.ArrayItemName) ? "item" : m_options.ArrayItemName); + m_jsonNamespace = m_nameTable.Add(m_options.NamespaceUri ?? string.Empty); + m_jsonPrefix = m_nameTable.Add(m_options.NodesPrefix ?? string.Empty); + m_xmlnsPrefix = m_nameTable.Add(XmlNameContext.XmlnsPrefix); + m_xmlnsNamespace = m_nameTable.Add(XmlNameContext.XmlnsNamespace); + m_xsiPrefix = m_nameTable.Add(XmlNameContext.XsiPrefix); + m_xsiNamespace = m_nameTable.Add(XmlNameContext.XsiNamespace); + + // TODO validate m_jsonRootName, m_jsonArrayItemName + + m_context = new XmlNameContext(null); + } + + public override int AttributeCount { + get { + return m_attributes == null ? 0 : m_attributes.Length; + } + } + + public override string BaseURI { + get { + return string.Empty; + } + } + + public override int Depth { + get { + return m_xmlDepth; + } + } + + public override bool EOF { + get { + return m_position == JsonXmlReaderPosition.Eof; + } + } + + public override bool IsEmptyElement { + get { return m_isEmpty; } + } + + + public override string LocalName { + get { + return m_qName.Name; + } + } + + public override string NamespaceURI { + get { + return m_qName.Namespace; + } + } + + public override XmlNameTable NameTable { + get { + return m_nameTable; + } + } + + public override XmlNodeType NodeType { + get { + return m_nodeType; + } + } + + public override string Prefix { + get { + return m_prefix; + } + } + + public override ReadState ReadState { + get { + switch (m_position) { + case JsonXmlReaderPosition.Initial: + return ReadState.Initial; + case JsonXmlReaderPosition.Eof: + return ReadState.EndOfFile; + case JsonXmlReaderPosition.Closed: + return ReadState.Closed; + case JsonXmlReaderPosition.Error: + return ReadState.Error; + default: + return ReadState.Interactive; + }; + } + } + + public override string Value { + get { + return ConvertValueToString(m_value); + } + } + + static string ConvertValueToString(object value) { + if (value == null) + return string.Empty; + + switch (Convert.GetTypeCode(value)) { + case TypeCode.Double: + return ((double)value).ToString(CultureInfo.InvariantCulture); + case TypeCode.String: + return (string)value; + case TypeCode.Boolean: + return (bool)value ? "true" : "false"; + default: + return value.ToString(); + } + } + + public override string GetAttribute(int i) { + Safe.ArgumentInRange(i, 0, AttributeCount - 1, nameof(i)); + return ConvertValueToString(m_attributes[i].Value); + } + + public override string GetAttribute(string name) { + if (m_attributes == null) + return null; + var qName = m_context.Resolve(name); + var attr = Array.Find(m_attributes, x => x.QName == qName); + var value = ConvertValueToString(attr?.Value); + return value == string.Empty ? null : value; + } + + public override string GetAttribute(string name, string namespaceURI) { + if (m_attributes == null) + return null; + var qName = new XmlQualifiedName(name, namespaceURI); + var attr = Array.Find(m_attributes, x => x.QName == qName); + var value = ConvertValueToString(attr?.Value); + return value == string.Empty ? null : value; + } + + public override string LookupNamespace(string prefix) { + return m_context.ResolvePrefix(prefix); + } + + public override bool MoveToAttribute(string name) { + if (m_attributes == null || m_attributes.Length == 0) + return false; + + var qName = m_context.Resolve(name); + var index = Array.FindIndex(m_attributes, x => x.QName == qName); + if (index >= 0) { + MoveToAttributeImpl(index); + return true; + } + return false; + } + + public override bool MoveToAttribute(string name, string ns) { + if (m_attributes == null || m_attributes.Length == 0) + return false; + + var qName = m_context.Resolve(name); + var index = Array.FindIndex(m_attributes, x => x.QName == qName); + if (index >= 0) { + MoveToAttributeImpl(index); + return true; + } + return false; + } + + void MoveToAttributeImpl(int i) { + if (!m_isAttribute) { + m_elementQName = m_qName; + m_elementDepth = m_xmlDepth; + m_elementPrefix = m_prefix; + m_elementIsEmpty = m_isEmpty; + m_isAttribute = true; + } + + var attr = m_attributes[i]; + + + m_currentAttribute = i; + m_currentAttributeRead = false; + m_nodeType = XmlNodeType.Attribute; + + m_xmlDepth = m_elementDepth + 1; + m_qName = attr.QName; + m_value = attr.Value; + m_prefix = attr.Prefix; + } + + public override bool MoveToElement() { + if (m_isAttribute) { + m_value = null; + m_nodeType = XmlNodeType.Element; + m_xmlDepth = m_elementDepth; + m_prefix = m_elementPrefix; + m_qName = m_elementQName; + m_isEmpty = m_elementIsEmpty; + m_isAttribute = false; + return true; + } + return false; + } + + public override bool MoveToFirstAttribute() { + if (m_attributes != null && m_attributes.Length > 0) { + MoveToAttributeImpl(0); + return true; + } + return false; + } + + public override bool MoveToNextAttribute() { + if (m_isAttribute) { + var next = m_currentAttribute + 1; + if (next < AttributeCount) { + MoveToAttributeImpl(next); + return true; + } + return false; + } else { + return MoveToFirstAttribute(); + } + + } + + public override bool ReadAttributeValue() { + if (!m_isAttribute || m_currentAttributeRead) + return false; + + ValueNode(m_attributes[m_currentAttribute].Value); + m_currentAttributeRead = true; + return true; + } + + public override void ResolveEntity() { + /* do nothing */ + } + + /// <summary> + /// Determines do we need to increase depth after the current node + /// </summary> + /// <returns></returns> + public bool IsSibling() { + switch (m_nodeType) { + case XmlNodeType.None: // start document + case XmlNodeType.Attribute: // after attribute only it's content can be iterated with ReadAttributeValue method + return false; + case XmlNodeType.Element: + // if the elemnt is empty the next element will be it's sibling + return m_isEmpty; + + case XmlNodeType.Document: + case XmlNodeType.DocumentFragment: + case XmlNodeType.Entity: + case XmlNodeType.Text: + case XmlNodeType.CDATA: + case XmlNodeType.EntityReference: + case XmlNodeType.ProcessingInstruction: + case XmlNodeType.Comment: + case XmlNodeType.DocumentType: + case XmlNodeType.Notation: + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + case XmlNodeType.EndElement: + case XmlNodeType.EndEntity: + case XmlNodeType.XmlDeclaration: + default: + return true; + } + } + + void ValueNode(object value) { + if (!IsSibling()) // the node is nested + m_xmlDepth++; + + m_qName = XmlQualifiedName.Empty; + m_nodeType = XmlNodeType.Text; + m_prefix = string.Empty; + m_value = value; + m_isEmpty = false; + m_attributes = null; + } + + void ElementNode(string name, string ns, XmlSimpleAttribute[] attrs, bool empty) { + if (!IsSibling()) // the node is nested + m_xmlDepth++; + + m_context = new XmlNameContext(m_context); + List<XmlSimpleAttribute> definedAttrs = null; + + // define new namespaces + if (attrs != null) { + foreach (var attr in attrs) { + if (attr.QName.Name == "xmlns") { + m_context.DefinePrefix(ConvertValueToString(attr.Value), string.Empty); + } else if (attr.Prefix == m_xmlnsPrefix) { + m_context.DefinePrefix(ConvertValueToString(attr.Value), attr.QName.Name); + } else { + string attrPrefix; + if (string.IsNullOrEmpty(attr.QName.Namespace)) + continue; + + // auto-define prefixes + if (!m_context.LookupNamespacePrefix(attr.QName.Namespace, out attrPrefix) || string.IsNullOrEmpty(attrPrefix)) { + // new namespace prefix added + attrPrefix = m_context.CreateNamespacePrefix(attr.QName.Namespace); + attr.Prefix = attrPrefix; + + if (definedAttrs == null) + definedAttrs = new List<XmlSimpleAttribute>(); + + definedAttrs.Add(new XmlSimpleAttribute(attrPrefix, m_xmlnsNamespace, m_xmlnsPrefix, attr.QName.Namespace)); + } + } + } + } + + string p; + // auto-define prefixes + if (!m_context.LookupNamespacePrefix(ns, out p)) { + p = m_context.CreateNamespacePrefix(ns); + if (definedAttrs == null) + definedAttrs = new List<XmlSimpleAttribute>(); + + definedAttrs.Add(new XmlSimpleAttribute(p, m_xmlnsNamespace, m_xmlnsPrefix, ns)); + } + + if (definedAttrs != null) { + if (attrs != null) + definedAttrs.AddRange(attrs); + attrs = definedAttrs.ToArray(); + } + + m_nodeType = XmlNodeType.Element; + m_qName = new XmlQualifiedName(name, ns); + m_prefix = p; + m_value = null; + m_isEmpty = empty; + m_attributes = attrs; + } + + void EndElementNode(string name, string ns) { + if (IsSibling()) // closing the element which has children + m_xmlDepth--; + + string p; + if (!m_context.LookupNamespacePrefix(ns, out p)) + throw new Exception($"Failed to lookup namespace '{ns}'"); + + m_context = m_context.ParentContext; + m_nodeType = XmlNodeType.EndElement; + m_prefix = p; + m_qName = new XmlQualifiedName(name, ns); + m_value = null; + m_attributes = null; + m_isEmpty = false; + } + + void XmlDeclaration() { + if (!IsSibling()) // the node is nested + m_xmlDepth++; + m_nodeType = XmlNodeType.XmlDeclaration; + m_qName = new XmlQualifiedName("xml"); + m_value = "version='1.0'"; + m_prefix = string.Empty; + m_attributes = null; + m_isEmpty = false; + } + + public override bool Read() { + try { + string elementName; + XmlSimpleAttribute[] elementAttrs = null; + MoveToElement(); + + switch (m_position) { + case JsonXmlReaderPosition.Initial: + m_jsonLocalName = m_jsonRootName; + m_jsonSkip = false; + XmlDeclaration(); + m_position = JsonXmlReaderPosition.Declaration; + return true; + case JsonXmlReaderPosition.Declaration: + elementAttrs = new[] { + new XmlSimpleAttribute(m_xsiPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_xsiNamespace), + string.IsNullOrEmpty(m_jsonPrefix) ? + new XmlSimpleAttribute(m_xmlnsPrefix, string.Empty, string.Empty, m_jsonNamespace) : + new XmlSimpleAttribute(m_jsonPrefix, m_xmlnsNamespace, m_xmlnsPrefix, m_jsonNamespace) + }; + break; + case JsonXmlReaderPosition.ValueElement: + if (!m_isEmpty) { + ValueNode(m_parser.ElementValue); + m_position = JsonXmlReaderPosition.ValueContent; + return true; + } else { + m_position = JsonXmlReaderPosition.ValueEndElement; + break; + } + case JsonXmlReaderPosition.ValueContent: + EndElementNode(m_jsonValueName, m_jsonNamespace); + m_position = JsonXmlReaderPosition.ValueEndElement; + return true; + case JsonXmlReaderPosition.Eof: + case JsonXmlReaderPosition.Closed: + case JsonXmlReaderPosition.Error: + return false; + } + + while (m_parser.Read()) { + var jsonName = m_nameTable.Add(m_parser.ElementName); + + switch (m_parser.ElementType) { + case JSONElementType.BeginObject: + if (!EnterJsonObject(jsonName, out elementName)) + continue; + + m_position = JsonXmlReaderPosition.BeginObject; + ElementNode(elementName, m_jsonNamespace, elementAttrs, false); + break; + case JSONElementType.EndObject: + if (!LeaveJsonScope(out elementName)) + continue; + + m_position = JsonXmlReaderPosition.EndObject; + EndElementNode(elementName, m_jsonNamespace); + break; + case JSONElementType.BeginArray: + if (!EnterJsonArray(jsonName, out elementName)) + continue; + + m_position = JsonXmlReaderPosition.BeginArray; + ElementNode(elementName, m_jsonNamespace, elementAttrs, false); + break; + case JSONElementType.EndArray: + if (!LeaveJsonScope(out elementName)) + continue; + + m_position = JsonXmlReaderPosition.EndArray; + EndElementNode(elementName, m_jsonNamespace); + break; + case JSONElementType.Value: + if (!VisitJsonValue(jsonName, out m_jsonValueName)) + continue; + + m_position = JsonXmlReaderPosition.ValueElement; + if (m_parser.ElementValue == null) + // generate empty element with xsi:nil="true" attribute + ElementNode( + m_jsonValueName, + m_jsonNamespace, + new[] { + new XmlSimpleAttribute("nil", m_xsiNamespace, m_xsiPrefix, true) + }, + true + ); + else + ElementNode(m_jsonValueName, m_jsonNamespace, elementAttrs, m_parser.ElementValue as string == string.Empty); + break; + default: + throw new Exception($"Unexpected JSON element {m_parser.ElementType}: {m_parser.ElementName}"); + } + return true; + } + + m_position = JsonXmlReaderPosition.Eof; + return false; + } catch { + m_position = JsonXmlReaderPosition.Error; + throw; + } + } + + void SaveJsonName() { + m_jsonNameStack.Push(new JsonContext { + skip = m_jsonSkip, + localName = m_jsonLocalName + }); + + } + + bool EnterJsonObject(string name, out string elementName) { + SaveJsonName(); + m_jsonSkip = false; + + if (string.IsNullOrEmpty(name)) { + if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays) + m_jsonLocalName = m_jsonArrayItemName; + } else { + m_jsonLocalName = name; + } + + elementName = m_jsonLocalName; + return true; + } + + /// <summary> + /// Called when JSON parser visits BeginArray ('[') element. + /// </summary> + /// <param name="name">Optional property name if the array is the member of an object</param> + /// <returns>true if element should be emited, false otherwise</returns> + bool EnterJsonArray(string name, out string elementName) { + SaveJsonName(); + + if (string.IsNullOrEmpty(name)) { + // m_jsonNameStack.Count == 1 means the root node + if (m_jsonNameStack.Count != 1 && !m_jsonFlattenArrays) + m_jsonLocalName = m_jsonArrayItemName; + + m_jsonSkip = false; // we should not flatten arrays inside arrays or in the document root + } else { + m_jsonLocalName = name; + m_jsonSkip = m_jsonFlattenArrays; + } + elementName = m_jsonLocalName; + + return !m_jsonSkip; + } + + bool VisitJsonValue(string name, out string elementName) { + if (string.IsNullOrEmpty(name)) { + // m_jsonNameStack.Count == 0 means that JSON document consists from simple value + elementName = (m_jsonNameStack.Count == 0 || m_jsonFlattenArrays) ? m_jsonLocalName : m_jsonArrayItemName; + } else { + elementName = name; + } + return true; + } + + bool LeaveJsonScope(out string elementName) { + elementName = m_jsonLocalName; + var skip = m_jsonSkip; + + var prev = m_jsonNameStack.Pop(); + m_jsonLocalName = prev.localName; + m_jsonSkip = prev.skip; + + return !skip; + } + + public override string ToString() { + switch (NodeType) { + case XmlNodeType.Element: + return $"<{Name} {string.Join(" ", (m_attributes ?? new XmlSimpleAttribute[0]).Select(x => $"{x.Prefix}{(string.IsNullOrEmpty(x.Prefix) ? "" : ":")}{x.QName.Name}='{ConvertValueToString(x.Value)}'"))} {(IsEmptyElement ? "/" : "")}>"; + case XmlNodeType.Attribute: + return $"@{Name}"; + case XmlNodeType.Text: + return $"{Value}"; + case XmlNodeType.CDATA: + return $"<![CDATA[{Value}]]>"; + case XmlNodeType.EntityReference: + return $"&{Name};"; + case XmlNodeType.EndElement: + return $"</{Name}>"; + default: + return $".{NodeType} {Name} {Value}"; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/JsonXmlReaderOptions.cs Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,66 @@ + +using System; +using System.Xml; + +namespace Implab.Formats.JSON { + /// <summary> + /// Набор необязательных параметров для <see cref="JSONXmlReader"/>, позволяющий управлять процессом + /// интерпретации <c>JSON</c> документа. + /// </summary> + public class JSONXmlReaderOptions : ICloneable { + /// <summary> + /// Пространство имен в котором будут располагаться читаемые элементы документа + /// </summary> + public string NamespaceURI { + get; + set; + } + + /// <summary> + /// Интерпретировать массивы как множественные элементы (убирает один уровень вложенности), иначе массив + /// представляется в виде узла, дочерними элементами которого являются элементы массива, имена дочерних элементов + /// определяются свойством <see cref="ArrayItemName"/>. По умолчанию <c>false</c>. + /// </summary> + public bool FlattenArrays { + get; + set; + } + + /// <summary> + /// Префикс, для узлов документа + /// </summary> + public string NodesPrefix { + get; + set; + } + + /// <summary> + /// Имя корневого элемента в xml документе + /// </summary> + public string RootName { + get; + set; + } + + /// <summary> + /// Имя элемента для массивов, если не включена опция <see cref="FlattenArrays"/>. + /// По умолчанию <c>item</c>. + /// </summary> + public string ArrayItemName { + get; + set; + } + + /// <summary> + /// Таблица атомизированных строк для построения документа. + /// </summary> + public XmlNameTable NameTable { + get; + set; + } + + public object Clone() { + return MemberwiseClone(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/JsonXmlReaderPosition.cs Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Implab.Xml { + public enum JsonXmlReaderPosition { + Initial, + Declaration, + BeginArray, + BeginObject, + EndArray, + EndObject, + ValueElement, + ValueContent, + ValueEndElement, + Eof, + Closed, + Error + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/XmlNameContext.cs Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Implab.Xml { + public class XmlNameContext { + public const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/"; + public const string XmlnsPrefix = "xmlns"; + public const string XmlNamespace = "http://www.w3.org/XML/1998/namespace"; + public const string XmlPrefix = "xml"; + public const string XsiNamespace = "http://www.w3.org/2001/XMLSchema-instance"; + public const string XsiPrefix = "xsi"; + + readonly static char[] _qNameDelim = new[] { ':' }; + + Dictionary<string, string> m_ns2prefix; + Dictionary<string, string> m_prefix2ns; + int m_nextPrefix = 1; + + public XmlNameContext ParentContext { get; private set; } + + public XmlNameContext(XmlNameContext parent) { + ParentContext = parent; + if (parent == null) { + DefinePrefixNoCheck(XmlnsNamespace, XmlnsPrefix); + DefinePrefixNoCheck(XmlNamespace, XmlPrefix); + } else { + m_nextPrefix = parent.m_nextPrefix; + } + } + + public bool LookupNamespacePrefix(string ns, out string prefix) { + if (ns == null) + ns = string.Empty; + + prefix = null; + for (var ctx = this; ctx != null; ctx = ctx.ParentContext) { + if (ctx.m_ns2prefix?.TryGetValue(ns, out prefix) == true) { + if (ctx != this) // cache for the future use + DefinePrefixNoCheck(ns, prefix); + return true; + } + } + return false; + } + + public string CreateNamespacePrefix(string ns) { + var prefix = $"p{m_nextPrefix++}"; + DefinePrefixNoCheck(ns, prefix); + return prefix; + } + + void DefinePrefixNoCheck(string ns, string prefix) { + if (ns == null) + ns = string.Empty; + if (prefix == null) + prefix = string.Empty; + + if (m_ns2prefix == null) + m_ns2prefix = new Dictionary<string, string>(); + m_ns2prefix[ns] = prefix; + + if (m_prefix2ns == null) + m_prefix2ns = new Dictionary<string, string>(); + m_prefix2ns[prefix] = ns; + } + + public void DefinePrefix(string ns, string prefix) { + // according to https://www.w3.org/TR/xml-names/#ns-decl + + // It MUST NOT be declared . Other prefixes MUST NOT be bound to this namespace name, and it MUST NOT be declared as the default namespace + if (ns == XmlnsNamespace) + throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'"); + + // It MAY, but need not, be declared, and MUST NOT be bound to any other namespace name + if (ns == XmlNamespace && prefix != XmlPrefix) + throw new Exception($"Attempt to define xmlns:{prefix}='{ns}'"); + + // add mapping + DefinePrefixNoCheck(ns, prefix); + } + + public string ResolvePrefix(string prefix) { + if (prefix == null) + prefix = string.Empty; + string ns = null; + for(var ctx = this; ctx != null; ctx = ctx.ParentContext) { + if (ctx.m_prefix2ns?.TryGetValue(prefix, out ns) == true) { + if (ctx != this) // cache for the future use + DefinePrefixNoCheck(ns, prefix); + return ns; + } + } + return null; + } + + public XmlQualifiedName Resolve(string name) { + Safe.ArgumentNotEmpty(name, nameof(name)); + var parts = name.Split(_qNameDelim, 2, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length == 2) { + return new XmlQualifiedName(parts[1], ResolvePrefix(parts[0])); + } else { + return new XmlQualifiedName(parts[0], ResolvePrefix(string.Empty)); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/XmlSimpleAttribute.cs Sat Sep 09 03:53:13 2017 +0300 @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +namespace Implab.Xml { + public class XmlSimpleAttribute { + public XmlSimpleAttribute(string name, string ns, string prefix, object value) { + QName = new XmlQualifiedName(name, ns); + Prefix = prefix; + Value = value; + } + + public XmlQualifiedName QName { get; set; } + + public string Prefix { get; set; } + + public object Value { get; set; } + } +}