Skip to content

feat(codegen): namespace-scoped type naming via --namespace-prefixes - #12

Open
tnymlr wants to merge 1 commit into
way-platform:mainfrom
tnymlr:feat/namespace-scoped-types-clean
Open

tnymlr wants to merge 1 commit into
way-platform:mainfrom
tnymlr:feat/namespace-scoped-types-clean

Conversation

@tnymlr

@tnymlr tnymlr commented Apr 8, 2026

Copy link
Copy Markdown

Problem

When a WSDL contains multiple XML schemas (common in enterprise SOAP services), different schemas can define types with the same name in different namespaces. XSD handles this through namespace scoping, but since soap-go generates all types into a single Go package, these produce compile-time name collisions.

The natural solution would be to generate each namespace into its own Go package. However, XSD namespaces commonly have circular import dependencies (namespace A imports types from B, B imports types from A). This is valid in XSD and handled natively by Java/C#, but Go's package system forbids circular imports, making per-namespace packages impossible for most real-world WSDLs.

Solution

This PR adds a --namespace-prefixes flag that accepts a JSON file mapping namespace URIs to short prefixes:

{
  "http://example.com/core/types/v1": "Core",
  "http://example.com/billing/types/v1": "CB",
  "http://example.com/orders/types/v1": "COM"
}

When provided, every generated Go type is prefixed with its schema's namespace abbreviation:

// Without --namespace-prefixes (collides if both schemas define ProductOrder):
type ProductOrder struct { ... }

// With --namespace-prefixes:
type COM_ProductOrder struct { ... }
type CBI_ProductOrder struct { ... }

How it works

  1. xmlns capture: The Schema struct now captures xmlns:* declarations via an ExtraAttrs field, with a NamespacePrefixMap() helper to extract the prefix-to-URI mapping.

  2. Type declarations: When generating struct, enum, and inline anonymous types, the generator prepends the namespace prefix from the user-provided map.

  3. Type references: When a field references a type from another namespace (e.g., type="ns3:FlexAttr"), the generator resolves ns3 → namespace URI (via xmlns map) → short prefix (via user map) → Core_FlexAttr.

  4. XSD builtins excluded: References to xs:string, xs:int, etc. are never prefixed — only user-defined types get namespace scoping.

  5. Client code: Operation wrapper types in the generated client are prefixed using the WSDL's own target namespace.

What doesn't change

When --namespace-prefixes is not provided, behavior is identical to before. All existing tests pass unchanged.

Why not per-namespace packages?

I explored generating each namespace into its own Go package with proper cross-package imports. The prototype worked for WSDLs without circular namespace dependencies. However, enterprise WSDLs commonly have bidirectional namespace imports (e.g., a "common entities" schema imports from "billing", and "billing" imports from "common entities"). Go's prohibition on circular package imports makes this approach fundamentally incompatible with real-world XSD namespace graphs.

The namespace-prefix approach keeps everything in a single package while eliminating collisions through deterministic, user-controlled naming.

Usage

soap gen -i service.wsdl -d output/ -p mypackage --namespace-prefixes prefixes.json

The JSON file must map every namespace URI that appears as a targetNamespace in the WSDL's schemas. Namespaces not in the map will generate types without a prefix.

Also included

  • Fix: RawXML type declaration is now emitted only once per package (was duplicated in multi-schema WSDLs)

When generating Go types from multi-namespace WSDLs, type name
collisions occur because different XML schemas can define types with
the same name. This adds a --namespace-prefixes flag that accepts a
JSON file mapping namespace URIs to short prefixes.

With namespace prefixes enabled, every generated Go type is prefixed
with its schema's namespace abbreviation (e.g., CBE_FlexAttr,
COM_ProductOrder). This eliminates collisions while keeping type
names readable.

The implementation:
- Captures xmlns:* declarations from schema elements via ExtraAttrs
- Resolves cross-namespace type references using the xmlns prefix
  map to determine which namespace a type belongs to
- Prefixes type declarations, inline anonymous types, enum types,
  wrapper types, and client method signatures
- Skips XSD builtin types (xs:string, xs:int, etc.)
- No behavioral change when the flag is not provided

Also fixes RawXML type being emitted multiple times in multi-schema
WSDLs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant