diff --git a/README.md b/README.md
index 9799ad81..a0914cc2 100644
--- a/README.md
+++ b/README.md
@@ -195,6 +195,9 @@ Options:
default is false)
--dtd, --allowDtdParse
allow DTD parsing (default is false)
+ --ns, --namingScheme use the specified naming scheme for class and
+ property names (default is Pascal; can be:
+ Direct, Pascal, Legacy)
For use from code use the [library NuGet package](https://www.nuget.org/packages/XmlSchemaClassGenerator-beta/):
diff --git a/XmlSchemaClassGenerator.Console/Program.cs b/XmlSchemaClassGenerator.Console/Program.cs
index 6aee3ce0..c98c0d48 100644
--- a/XmlSchemaClassGenerator.Console/Program.cs
+++ b/XmlSchemaClassGenerator.Console/Program.cs
@@ -67,6 +67,7 @@ static int Main(string[] args)
var separateNamespaceHierarchy = false;
var serializeEmptyCollections = false;
var allowDtdParse = false;
+ NamingScheme? namingScheme = null;
var options = new OptionSet {
{ "h|help", "show this message and exit", v => showHelp = v != null },
@@ -123,20 +124,20 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
{ "csm|collectionSettersMode=", @"generate a private, public, or init-only setter
with or without backing field initialization for collections
(default is Private; can be: {Private, Public, PublicWithoutConstructorInitialization, Init, InitWithoutConstructorInitialization})",
- v =>
- {
- collectionSettersMode = v switch
- {
- "pr" or "Private" => CollectionSettersMode.Private,
- "pu" or "Public" => CollectionSettersMode.Public,
- "puwci" or "PublicWithoutConstructorInitialization" =>
- CollectionSettersMode.PublicWithoutConstructorInitialization,
- "in" or "Init" => CollectionSettersMode.Init,
- "inwci" or "InitWithoutConstructorInitialization" =>
- CollectionSettersMode.InitWithoutConstructorInitialization,
- _ => CollectionSettersMode.Private
- };
- }},
+ v =>
+ {
+ collectionSettersMode = v switch
+ {
+ "pr" or "Private" => CollectionSettersMode.Private,
+ "pu" or "Public" => CollectionSettersMode.Public,
+ "puwci" or "PublicWithoutConstructorInitialization" =>
+ CollectionSettersMode.PublicWithoutConstructorInitialization,
+ "in" or "Init" => CollectionSettersMode.Init,
+ "inwci" or "InitWithoutConstructorInitialization" =>
+ CollectionSettersMode.InitWithoutConstructorInitialization,
+ _ => CollectionSettersMode.Private
+ };
+ }},
{ "ctro|codeTypeReferenceOptions=", "the default CodeTypeReferenceOptions Flags to use (default is unset; can be: {GlobalReference, GenericTypeParameter})", v => codeTypeReferenceOptions = v == null ? default : (CodeTypeReferenceOptions)Enum.Parse(typeof(CodeTypeReferenceOptions), v, false) },
{ "tvpn|textValuePropertyName=", "the name of the property that holds the text value of an element (default is Value)", v => textValuePropertyName = v },
{ "dst|debuggerStepThrough", "generate DebuggerStepThroughAttribute (default is enabled)", v => generateDebuggerStepThroughAttribute = v != null },
@@ -162,6 +163,18 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
{ "uc|unionCommonType", "generate a common type for unions if possible (default is false)", v => unionCommonType = v != null },
{ "ec|serializeEmptyCollections", "serialize empty collections (default is false)", v => serializeEmptyCollections = v != null },
{ "dtd|allowDtdParse", "allows dtd parse (default is false)", v => allowDtdParse = v != null },
+ { "ns|namingScheme", @"use the specified naming scheme for class and property names (default is Pascal; can be: Direct, Pascal, Legacy)",
+ v =>
+ {
+ namingScheme = v?.ToLowerInvariant() switch
+ {
+ "direct" => NamingScheme.Direct,
+ "pascal" => NamingScheme.PascalCase,
+ "legacy" => NamingScheme.LegacyPascalCase,
+ _ => NamingScheme.PascalCase,
+ };
+ }
+ }
};
var globsAndUris = options.Parse(args);
@@ -222,7 +235,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
DateTimeWithTimeZone = dateTimeWithTimeZone,
EntityFramework = entityFramework,
GenerateInterfaces = interfaces,
- NamingScheme = pascal ? NamingScheme.PascalCase : NamingScheme.Direct,
+ NamingScheme = namingScheme != null ? namingScheme.Value : (pascal ? NamingScheme.PascalCase : NamingScheme.Direct),
AssemblyVisible = assembly,
CollectionType = collectionType,
CollectionImplementationType = collectionImplementationType,
@@ -250,7 +263,7 @@ A file name may be given by appending a pipe sign (|) followed by a file name (l
MapUnionToWidestCommonType = unionCommonType,
SeparateNamespaceHierarchy = separateNamespaceHierarchy,
SerializeEmptyCollections = serializeEmptyCollections,
- AllowDtdParse = allowDtdParse
+ AllowDtdParse = allowDtdParse,
};
if (nameSubstituteMap.Any())
diff --git a/XmlSchemaClassGenerator/CodeUtilities.cs b/XmlSchemaClassGenerator/CodeUtilities.cs
index 1819522e..550fb151 100644
--- a/XmlSchemaClassGenerator/CodeUtilities.cs
+++ b/XmlSchemaClassGenerator/CodeUtilities.cs
@@ -14,6 +14,14 @@ namespace XmlSchemaClassGenerator
{
public static class CodeUtilities
{
+ // Match non-letter followed by letter
+ private static readonly Regex PascalCaseRegex = new(@"[^\p{L}]\p{L}", RegexOptions.Compiled);
+
+ // Uppercases first letter and all letters following non-letters.
+ // Examples: testcase -> Testcase, html5element -> Html5Element, test_case -> Test_Case
+ public static string ToLegacyPascalCase(this string s) => string.IsNullOrEmpty(s) ? s
+ : char.ToUpperInvariant(s[0]) + PascalCaseRegex.Replace(s.Substring(1), m => m.Value[0] + char.ToUpperInvariant(m.Value[1]).ToString());
+
private static readonly Regex invalidCharsRgx = new(@"[^_\p{L}\p{N}]");
private static readonly Regex whiteSpace = new(@"(?<=\s)");
private static readonly Regex startsWithLowerCaseChar = new(@"^\p{Ll}");
diff --git a/XmlSchemaClassGenerator/NamingExtensions.cs b/XmlSchemaClassGenerator/NamingExtensions.cs
index 48104918..1c47ae31 100644
--- a/XmlSchemaClassGenerator/NamingExtensions.cs
+++ b/XmlSchemaClassGenerator/NamingExtensions.cs
@@ -106,6 +106,9 @@ public static string ToTitleCase(this string s, NamingScheme namingScheme)
case NamingScheme.PascalCase:
s = s.ToPascalCase();
break;
+ case NamingScheme.LegacyPascalCase:
+ s = s.ToLegacyPascalCase();
+ break;
}
return s.MakeValidIdentifier();
}
diff --git a/XmlSchemaClassGenerator/NamingProviders/SubstituteNamingProvider.cs b/XmlSchemaClassGenerator/NamingProviders/SubstituteNamingProvider.cs
index cba9a081..a9533adc 100644
--- a/XmlSchemaClassGenerator/NamingProviders/SubstituteNamingProvider.cs
+++ b/XmlSchemaClassGenerator/NamingProviders/SubstituteNamingProvider.cs
@@ -7,14 +7,26 @@ namespace XmlSchemaClassGenerator.NamingProviders
///
/// Provides options to customize member names, and automatically substitute names for defined types/members.
///
- public class SubstituteNamingProvider
- : NamingProvider, INamingProvider
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The naming scheme.
+ ///
+ /// A dictionary containing name substitute pairs.
+ ///
+ /// Keys need to be prefixed with an appropriate kind ID as documented at:
+ /// https://t.ly/HHEI.
+ ///
+ /// Prefix with A: to substitute any type/member.
+ ///
+ public class SubstituteNamingProvider(NamingScheme namingScheme, Dictionary nameSubstitutes)
+ : NamingProvider(namingScheme), INamingProvider
{
- private readonly Dictionary _nameSubstitutes;
+ private readonly Dictionary _nameSubstitutes = nameSubstitutes;
///
public SubstituteNamingProvider(NamingScheme namingScheme)
- : this(namingScheme, new())
+ : this(namingScheme, [])
{
}
@@ -24,24 +36,6 @@ public SubstituteNamingProvider(Dictionary nameSubstitutes)
{
}
- ///
- /// Initializes a new instance of the class.
- ///
- /// The naming scheme.
- ///
- /// A dictionary containing name substitute pairs.
- ///
- /// Keys need to be prefixed with an appropriate kind ID as documented at:
- /// https://t.ly/HHEI.
- ///
- /// Prefix with A: to substitute any type/member.
- ///
- public SubstituteNamingProvider(NamingScheme namingScheme, Dictionary nameSubstitutes)
- : base(namingScheme)
- {
- _nameSubstitutes = nameSubstitutes;
- }
-
///
public override string PropertyNameFromAttribute(string typeModelName, string attributeName, XmlSchemaAttribute attribute)
=> SubstituteName("P", base.PropertyNameFromAttribute(typeModelName, attributeName, attribute));
@@ -101,8 +95,7 @@ private string SubstituteName(string typeIdPrefix, string name)
return name;
}
- string substituteName;
- if (_nameSubstitutes.TryGetValue($"{typeIdPrefix}:{name}", out substituteName) || _nameSubstitutes.TryGetValue($"A:{name}", out substituteName))
+ if (_nameSubstitutes.TryGetValue($"{typeIdPrefix}:{name}", out string substituteName) || _nameSubstitutes.TryGetValue($"A:{name}", out substituteName))
{
return substituteName;
}
diff --git a/XmlSchemaClassGenerator/NamingScheme.cs b/XmlSchemaClassGenerator/NamingScheme.cs
index 38863996..0a3db60c 100644
--- a/XmlSchemaClassGenerator/NamingScheme.cs
+++ b/XmlSchemaClassGenerator/NamingScheme.cs
@@ -9,6 +9,7 @@ namespace XmlSchemaClassGenerator
public enum NamingScheme
{
Direct,
- PascalCase
+ PascalCase,
+ LegacyPascalCase,
}
}