diff --git a/XmlSchemaClassGenerator.Console/Program.cs b/XmlSchemaClassGenerator.Console/Program.cs index 773ab244..3390e2b6 100644 --- a/XmlSchemaClassGenerator.Console/Program.cs +++ b/XmlSchemaClassGenerator.Console/Program.cs @@ -117,7 +117,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l uris.AddRange(expandedGlob); } - var namespaceMap = namespaces.Select(n => ParseNamespace(n, namespacePrefix)).ToNamespaceProvider(key => + var namespaceMap = namespaces.Select(n => CodeUtilities.ParseNamespace(n, namespacePrefix)).ToNamespaceProvider(key => { var xn = key.XmlSchemaNamespace; var name = string.Join(".", xn.Split('/').Where(p => p != "schema" && GeneratorConfiguration.IdentifierRegex.IsMatch(p)) @@ -170,21 +170,6 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l generator.Generate(uris); } - static KeyValuePair ParseNamespace(string nsArg, string namespacePrefix) - { - var parts = nsArg.Split(new[] { '=' }, 2); - var xmlNs = parts[0]; - var netNs = parts[1]; - var parts2 = xmlNs.Split(new[] { '|' }, 2); - var source = parts2.Length == 2 ? new Uri(parts2[1], UriKind.RelativeOrAbsolute) : null; - xmlNs = parts2[0]; - if (!string.IsNullOrEmpty(namespacePrefix)) - { - netNs = namespacePrefix + "." + netNs; - } - return new KeyValuePair(new NamespaceKey(source, xmlNs), netNs); - } - static void ShowHelp(OptionSet p) { System.Console.WriteLine("Usage: dotnet xscgen [OPTIONS]+ xsdFile..."); diff --git a/XmlSchemaClassGenerator.Tests/NamespaceProviderTests.cs b/XmlSchemaClassGenerator.Tests/NamespaceProviderTests.cs index f65d2e36..949b96c1 100644 --- a/XmlSchemaClassGenerator.Tests/NamespaceProviderTests.cs +++ b/XmlSchemaClassGenerator.Tests/NamespaceProviderTests.cs @@ -63,5 +63,43 @@ public void NamespaceKeyComparableTest() Assert.False(new NamespaceKey("http://test") <= null); Assert.True(new NamespaceKey("http://test") != null); } + + [Theory] + [InlineData("http://www.w3.org/2001/XMLSchema", "test.xsd", "MyNamespace", "Test")] + [InlineData("http://www.w3.org/2001/XMLSchema", "test.xsd", "MyNamespace", null)] + [InlineData("http://www.w3.org/2001/XMLSchema", "test.xsd", "MyNamespace", "")] + [InlineData("", "test.xsd", "MyNamespace", "Test")] + [InlineData("", "test.xsd", "MyNamespace", null)] + [InlineData("", "test.xsd", "MyNamespace", "")] + [InlineData(null, "test.xsd", "MyNamespace", "Test")] + [InlineData(null, "test.xsd", "MyNamespace", null)] + [InlineData(null, "test.xsd", "MyNamespace", "")] + public void TestParseNamespaceUtilityMethod1(string xmlNs, string xmlSchema, string netNs, string netPrefix) + { + string customNsPattern = "{0}|{1}={2}"; + + var uri = new Uri(xmlSchema, UriKind.RelativeOrAbsolute); + var fullNetNs = (string.IsNullOrEmpty(netPrefix)) ? netNs : string.Join('.', netPrefix, netNs); + + var expected = new KeyValuePair(new NamespaceKey(uri, xmlNs), fullNetNs); + var actual = CodeUtilities.ParseNamespace(string.Format(customNsPattern, xmlNs, xmlSchema, netNs), netPrefix); + + Assert.Equal(expected, actual); + } + + [Theory] + [InlineData("test.xsd", "MyNamespace", "Test")] + [InlineData("test.xsd", "MyNamespace", null)] + [InlineData("test.xsd", "MyNamespace", "")] + public void TestParseNamespaceUtilityMethod2(string xmlSchema, string netNs, string netPrefix) + { + string customNsPattern = "{0}={1}"; + + var fullNetNs = (string.IsNullOrEmpty(netPrefix)) ? netNs : string.Join('.', netPrefix, netNs); + var expected = new KeyValuePair(new NamespaceKey(null, xmlSchema), fullNetNs); + var actual = CodeUtilities.ParseNamespace(string.Format(customNsPattern, xmlSchema, netNs), netPrefix); + + Assert.Equal(expected, actual); + } } } diff --git a/XmlSchemaClassGenerator.Tests/XmlTests.cs b/XmlSchemaClassGenerator.Tests/XmlTests.cs index c67ebc67..081fd00f 100644 --- a/XmlSchemaClassGenerator.Tests/XmlTests.cs +++ b/XmlSchemaClassGenerator.Tests/XmlTests.cs @@ -370,16 +370,77 @@ void TestCompareToXsd(Type t1, Type t2, string file) [Fact, TestPriority(1)] [UseCulture("en-US")] - public void TestBpmn() + public void TestCustomNamespaces() { + string customNsPattern = "|{0}={1}"; + string bpmnXsd = "BPMN20.xsd"; + string semantXsd = "Semantic.xsd"; + string bpmndiXsd = "BPMNDI.xsd"; + string dcXsd = "DC.xsd"; + string diXsd = "DI.xsd"; + + Dictionary xsdToCsharpNsMap = new Dictionary + { + { bpmnXsd, "Namespace1" }, + { semantXsd, "Namespace1" }, + { bpmndiXsd, "Namespace2" }, + { dcXsd, "Namespace3" }, + { diXsd, "Namespace4" } + }; + + Dictionary xsdToCsharpTypeMap = new Dictionary + { + { bpmnXsd, "TDefinitions" }, + { semantXsd, "TActivity" }, + { bpmndiXsd, "BPMNDiagram" }, + { dcXsd, "Font" }, + { diXsd, "DiagramElement" } + }; + + List customNamespaceConfig = new List(); + + foreach (var ns in xsdToCsharpNsMap) + customNamespaceConfig.Add(string.Format(customNsPattern, ns.Key, ns.Value)); + var assembly = Compiler.Generate("Bpmn", BpmnPattern, new Generator { DataAnnotationMode = DataAnnotationMode.All, GenerateNullables = true, - MemberVisitor = (member, model) => { } + MemberVisitor = (member, model) => { }, + NamespaceProvider = customNamespaceConfig.Select(n => CodeUtilities.ParseNamespace(n, null)).ToNamespaceProvider() }); Assert.NotNull(assembly); + Type type = null; + + type = assembly.GetTypes().SingleOrDefault(t => t.Name == xsdToCsharpTypeMap[bpmnXsd]); + Assert.NotNull(type); + Assert.Equal(xsdToCsharpNsMap[bpmnXsd], type.Namespace); + + type = assembly.GetTypes().SingleOrDefault(t => t.Name == xsdToCsharpTypeMap[semantXsd]); + Assert.NotNull(type); + Assert.Equal(xsdToCsharpNsMap[semantXsd], type.Namespace); + + type = assembly.GetTypes().SingleOrDefault(t => t.Name == xsdToCsharpTypeMap[bpmndiXsd]); + Assert.NotNull(type); + Assert.Equal(xsdToCsharpNsMap[bpmndiXsd], type.Namespace); + + type = assembly.GetTypes().SingleOrDefault(t => t.Name == xsdToCsharpTypeMap[dcXsd]); + Assert.NotNull(type); + Assert.Equal(xsdToCsharpNsMap[dcXsd], type.Namespace); + + type = assembly.GetTypes().SingleOrDefault(t => t.Name == xsdToCsharpTypeMap[diXsd]); + Assert.NotNull(type); + Assert.Equal(xsdToCsharpNsMap[diXsd], type.Namespace); + } + + [Fact, TestPriority(2)] + [UseCulture("en-US")] + public void TestBpmn() + { + var assembly = Compiler.Generate("Bpmn", BpmnPattern); + Assert.NotNull(assembly); + var type = assembly.GetTypes().SingleOrDefault(t => t.Name == "TDefinitions"); Assert.NotNull(type); diff --git a/XmlSchemaClassGenerator/CodeUtilities.cs b/XmlSchemaClassGenerator/CodeUtilities.cs index 167c95c7..51cd4133 100644 --- a/XmlSchemaClassGenerator/CodeUtilities.cs +++ b/XmlSchemaClassGenerator/CodeUtilities.cs @@ -297,5 +297,20 @@ internal static string NormalizeNewlines(string text) }; internal static Uri CreateUri(string uri) => string.IsNullOrEmpty(uri) ? null : new Uri(uri); + + public static KeyValuePair ParseNamespace(string nsArg, string namespacePrefix) + { + var parts = nsArg.Split(new[] { '=' }, 2); + var xmlNs = parts[0]; + var netNs = parts[1]; + var parts2 = xmlNs.Split(new[] { '|' }, 2); + var source = parts2.Length == 2 ? new Uri(parts2[1], UriKind.RelativeOrAbsolute) : null; + xmlNs = parts2[0]; + if (!string.IsNullOrEmpty(namespacePrefix)) + { + netNs = namespacePrefix + "." + netNs; + } + return new KeyValuePair(new NamespaceKey(source, xmlNs), netNs); + } } } \ No newline at end of file diff --git a/XmlSchemaClassGenerator/ModelBuilder.cs b/XmlSchemaClassGenerator/ModelBuilder.cs index 4be64d4c..46d0c27d 100644 --- a/XmlSchemaClassGenerator/ModelBuilder.cs +++ b/XmlSchemaClassGenerator/ModelBuilder.cs @@ -44,14 +44,52 @@ public ModelBuilder(GeneratorConfiguration configuration, XmlSchemaSet set) .DistinctBy(g => g.QualifiedName.ToString()) .ToDictionary(g => g.QualifiedName); - foreach (var globalType in set.GlobalTypes.Values.Cast()) + List dependencyOrder = new List(); + foreach (var schema in set.Schemas().Cast()) + { + ResolveDependencies(schema, dependencyOrder); + } + + foreach (var schema in dependencyOrder) + { + var types = set.GlobalTypes.Values.Cast().Where(s => s.GetSchema() == schema); + CreateTypes(types); + var elements = set.GlobalElements.Values.Cast().Where(s => s.GetSchema() == schema); + CreateElements(elements); + } + } + + private void ResolveDependencies(XmlSchema schema, List dependencyOrder) + { + if (dependencyOrder.Contains(schema)) + return; + + var imports = schema.Includes.OfType(); + if (imports.Any()) + { + foreach (var import in imports) + { + if (import.Schema != null) + ResolveDependencies(import.Schema, dependencyOrder); + } + } + dependencyOrder.Add(schema); + } + + + private void CreateTypes(IEnumerable types) + { + foreach (var globalType in types) { var schema = globalType.GetSchema(); var source = CodeUtilities.CreateUri(schema?.SourceUri); CreateTypeModel(source, globalType, globalType.QualifiedName); } + } - foreach (var rootElement in set.GlobalElements.Values.Cast()) + private void CreateElements(IEnumerable elements) + { + foreach (var rootElement in elements) { var rootSchema = rootElement.GetSchema(); var source = CodeUtilities.CreateUri(rootSchema.SourceUri); diff --git a/XmlSchemaClassGenerator/NamespaceProvider.cs b/XmlSchemaClassGenerator/NamespaceProvider.cs index fffe5b2f..753b9146 100644 --- a/XmlSchemaClassGenerator/NamespaceProvider.cs +++ b/XmlSchemaClassGenerator/NamespaceProvider.cs @@ -158,6 +158,9 @@ public string FindNamespace(NamespaceKey key, string defaultNamespace = null) var path = key.Source.IsAbsoluteUri ? key.Source.LocalPath : key.Source.OriginalString; keyValues.Add(new NamespaceKey(new Uri(Path.GetFileName(path), UriKind.Relative))); + // Search for both file name and XmlSchemaNamespace pair + keyValues.Add(new NamespaceKey(new Uri(Path.GetFileName(path), UriKind.Relative), key.XmlSchemaNamespace)); + // Search for XmlSchemaNamespace only keyValues.Add(new NamespaceKey(key.XmlSchemaNamespace));