Skip to content

v3.4.24 - LinqToXsd CLI changes, fixes a bug with xs:choice - #104

Draft
mamift wants to merge 63 commits into
masterfrom
testing/dublincore-xhtml
Draft

mamift wants to merge 63 commits into
masterfrom
testing/dublincore-xhtml

Conversation

@mamift

@mamift mamift commented Sep 16, 2026 •

Copy link
Copy Markdown
Owner
  • Includes PR fix bug with creation of xs:choices of repeatable elements #105 (fix bug with creation of xs:choices of repeatable elements)
  • The linqtoxsd CLI tool behaviour has changed:
    • the -a flag will now delete existing files with the .xsd.cs extension (since v3.4.17, the default output extension is now .xsd-g.cs) when outputting code
    • fixed a bug with getting the actual XML Schema version, the xs:schema\@version attribute is a user defined attribute for the schema version, it does not specify which W3C XSD specification version to validate against (i.e. v1.0 or v1.1). This would've inadvertently caused linqtoxsd to skip schemas whose versions were v1.1 or higher.
    • the behaviour of the -a argument when invoking the linqtoxsd CLI tool is now always applied i.e. it now always searches for a .xsd.config file regardless. However, in previous versions, if a config file was not found, linqtoxsd would skip that XSD file - now linqtoxsd will simply apply default config values for those XSD files.

v3.4.24

…ut its presence does not throw an error to enable backward compatibility with existing scripts.
… (since 3.4.17, the default output extension is now .xsd-g.cs).
@mamift mamift changed the title Testing/dublincore xhtml LinqToXsd CLI changes (v3.4.24) Sep 22, 2026
@mamift mamift changed the title LinqToXsd CLI changes (v3.4.24) v3.4.24 - LinqToXsd CLI changes, fixes another codegen bug with xs:choice Sep 22, 2026
@mamift mamift changed the title v3.4.24 - LinqToXsd CLI changes, fixes another codegen bug with xs:choice v3.4.24 - LinqToXsd CLI changes, fixes a bug with xs:choice Sep 22, 2026
@mamift
mamift force-pushed the testing/dublincore-xhtml branch from 1bd25c2 to 5d8961c Compare September 22, 2026 03:31
mamift and others added 30 commits September 29, 2026 20:47
…en global:: is used in other places for namespaces with Xml in them.
…ks for XTCE but needs something more thorough for namespace checking.
…tp urls, then LinqToXsd will now preload those Xsd files from embedded resources, rather than make outbound HTTP calls.
XSD enumerations whose values consist entirely of non-alphanumeric
characters (e.g. ComparisonOperatorsType in the XTCE schema with values
==, !=, <, <=, >, >=) previously had every character replaced with '_'
when building the C# member name. Distinct values such as "==", "!="
and "<=" therefore all collapsed onto the same member "__", producing
duplicate enum members and CS0102 compile errors in the generated code.

EnumFacet.CreateValidIdentifier now detects values composed entirely of
characters that cannot appear in a C# identifier and expands each
character to its full English word name instead of replacing it with an
underscore, so "==" becomes EqualsEquals, "<=" becomes LessThanEquals,
"&&" becomes AmpersandAmpersand and so on. Mixed values (e.g. "en-fr",
which contains letters) keep the legacy underscore replacement, so
existing generated code is unaffected.

NameGenerator.ExpandSymbolToFullWord is backed by a new lookup table
exposed via TryExpandSymbolToFullWord, extended with ASCII whitespace
and common Unicode symbols (e.g. +/- signs, sqrt, section sign), and no
longer throws for unknown single symbols - such values degrade
gracefully to the legacy underscore replacement instead of failing code
generation.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Even with symbol-to-word expansion, distinct schema values can still map
onto the same candidate member name: "*" expands to "Asterisk", which
collides with a literal "Asterisk" value, and "a-b" and "a_b" both
produce "a_b". Previously such values would be emitted as duplicate enum
members and the generated code would not compile.

EnumFacet.CreateUniqueFacets now maps the distinct values of an
enumeration to facets with mutually unique member names. When candidate
names collide, the facet whose schema value is already a valid
identifier keeps the name and the others are disambiguated with a
deterministic FNV-1a-derived suffix of the schema value, so the result
does not depend on facet ordering or on the unstable
String.GetHashCode. A final pass falls back to numbered suffixes for
crafted schemas that can still collide.

The same routine now backs both consumers of facet members, so the
generated enum fields and the 'value:member' strings embedded in the
RestrictionFacets of the generated validator always agree:
- GetEnumFacets (used by TypeBuilder.CreateEnumType and
  TypesToCodeDom to emit enum members)
- CompiledFacets.compileFacets (used to emit the validator facet
  strings); it now collects the enumeration values first and builds the
  facet strings once the whole set is known. Non-enum restrictions
  (e.g. numeric enumerations) still add their typed values immediately,
  as before.

Co-Authored-By: Claude Code <noreply@anthropic.com>
EnumFacetMapping.Parse previously split the stored "value:member"
enumeration strings on the first colon, so an enumeration value that
itself contains a colon (e.g. "12:30") was truncated to "12" at runtime,
breaking validation and round-tripping. Member names are valid C#
identifiers and can never contain a colon, so the last colon is always
the true separator. Parsing now keeps everything before the last colon
as the schema value, which is backwards compatible with all previously
generated mapping strings (they contain at most one colon).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Regenerated with the fixed code generator. The ComparisonOperatorsType
and MathOperatorsType enums no longer collapse their operator values
onto duplicate "__" members, which previously caused CS0102 errors and
broke the XTCE project (and therefore the test suite) build:

- ComparisonOperatorsType: == -> EqualsEquals, != -> ExclamationMarkEquals,
  <= -> LessThanEquals, >= -> GreaterThanEquals
- MathOperatorsType: << -> LessThanLessThan, >> -> GreaterThanGreaterThan,
  && -> AmpersandAmpersand, || -> PipePipe, >= -> GreaterThanEquals,
  <= -> LessThanEquals, == -> EqualsEquals, != -> ExclamationMarkEquals

The 'value:member' strings in the generated validators were updated in
lockstep, and the default value references now point at the renamed
members. The XTCE project now compiles cleanly.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Adds test fixtures to EnumsTest.xsd and regenerates the committed
EnumsTest.xsd-g.cs with the fixed generator:

- OperatorEnum: values ==, !=, <, <=, >, >=, &&, || (all-symbol values
  that previously collapsed onto duplicate "__" members)
- ColonContainingEnum: values 12:30 and 23:59 (values containing the
  'value:member' separator colon, exercising the last-colon split in
  EnumFacetMapping)
- CollisionEnum: values * and Asterisk (two distinct values that map to
  the same candidate member name, exercising EnumFacet.CreateUniqueFacets;
  "*" is disambiguated to "Asterisk_2F0C9F3D" while the literal
  "Asterisk" value keeps its name)
- OperatorElementType: element wrapper for runtime round-trip tests

The regeneration is purely additive apart from the generator version
bump in the header comment (the committed file predates 3.4.24); all
previously generated types are unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
EnumFacetTests (unit tests against the generator's facet model):
- symbol-only values expand each symbol to its full word name
- single symbols map to their full word names
- mixed values keep the legacy underscore replacement, so existing
  generated code is unaffected
- keyword values are escaped with '@'
- unknown symbols degrade gracefully to underscore replacement
- colliding facets are disambiguated with unique, order-independent
  member names, and duplicate values produce a single facet

OperatorEnumTests (integration tests against the EnumsTest schema):
- generated code contains the expected unique word members for the
  operator enum, and unique members for the collision enum
- runtime round-trips verify that every operator value is written to
  XML using its original schema value and parsed back to the correct
  member, that colon-containing values survive the 'value:member'
  split, and that the disambiguated collision member maps back to its
  original symbol value

The new test classes are self-contained and do not share the
EnumsCodeGenTest fixture, whose generated-tree compilation diagnostics
currently warn (and therefore skip) every test in that class; that
fixture's behaviour predates and is unrelated to this change.

Co-Authored-By: Claude Code <noreply@anthropic.com>
CreateTypeManager emits using imports between the generated code namespaces
(each namespace imports the root namespace and vice versa) but never applied
the global:: prefixing decision that AddDefaultImports applies to the
standard imports. With AlwaysPrefixGlobal="true" those imports were still
emitted bare, e.g. `using urn.din.Item70121.Item2012.MsgDef;` inside
`namespace www.w3.org.Item2000.Item09.xmldsig`, so the setting was only
partially honoured. In auto-detection mode the same ambiguity the default
imports guard against - an import sharing a component with the enclosing
namespace, which can make the compiler resolve it relative to that namespace
- also applied to these imports but was never checked.

AddGlobalPrefixIfRequired mirrors AddDefaultImports' decision: prefix
global:: when AlwaysPrefixGlobalInUsingDirectives is set, otherwise when the
import and the importing namespace share a component (case-insensitive).
The root import was previously one CodeNamespaceImport instance shared
across every namespace; it is now created per target namespace so each
import is decided against the namespace it is declared in.

Default-import behaviour is unchanged, so fixtures whose namespaces share no
components with their imports generate identical output to before.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Adds GlobalPrefixUsingDirectivesTests, a self-contained codegen test class
that generates code for a schema set with two namespaces
(urn:test:namespace1 importing urn:test:namespace2) and inspects the
emitted using directives.

WhenAlwaysPrefixGlobalThenAllUsingDirectivesAreGlobalQualified covers the
AlwaysPrefixGlobal="true" setting: every using directive must be qualified
with global::.

WhenImportSharesComponentWithImportingNamespaceThenImportIsGlobalQualified
covers auto-detection: the cross-namespace imports share the urn and test
components with the namespaces they are declared in, so they must be
qualified, while the default imports must remain unqualified.

The tests use in-memory schemas and do not inherit the shared codegen
fixture, following the precedent set by OperatorEnumTests, because that
fixture marks its tests as skipped when its harness compilation reports
diagnostics.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Regenerated output for the fixtures whose cross-namespace using directives
changed: imports added between generated code namespaces are now qualified
with global:: when AlwaysPrefixGlobal is set (W3C XMLSchema v1,
ImportsXmlAttributes) or when the import shares a component with the
enclosing namespace (Multi-namespaces, LegalRuleML, ORMML, Microsoft Search
Response, SOAP-WSDL). No other generated code changed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
… up to a hitherto unknown bug that was fixed coincidentally.
…ant code fix; there were two 'Stainless Steel 1.4401:Stainless_Steel_1_4401' entries before and now there's just one (the XSD also has two entries for 'Stainless Steel 1.4401:Stainless_Steel_1_4401' in cidxListEnclosureType XSD enum type.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant