Namespace support: naming collisions / redeclared types in different namespaces
w65536 opened this issue · 1 comments
This issue refers to one particular problem with namespaces, but I assume it should be looked at in a broader context of namespaces in general.
I start with describing the particular problem of name collisions and will address the more general topic at the end.
Scenario
This is a real scenario. Let's assume we have a service svcX with two additional services, svc1 and svc2, each in a separate namespace. All the three happen to use the same element names, "duplicateElement" and "duplicateComplexType", which is valid since they are in separate namespaces.
types.xsd (namespace for svcX http://www.example.com/svcX) imports the other two namespaces located in svc1.xsd (http://www.example.com/svc1) and svc2.xsd (http://www.example.com/svc2). (I omit representing the .wsdl file here since it does not add any value for understanding the problem.) These definitions result in the generated Go code in example.go at the end.
types.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.com/svcX" xmlns:svc1="http://www.example.com/svc1" xmlns:svc2="http://www.example.com/svc2" targetNamespace="http://www.example.com/svcX" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1">
<xs:import namespace="http://www.example.com/svc1" schemaLocation="svc1.xsd"/>
<xs:import namespace="http://www.example.com/svc2" schemaLocation="svc2.xsd"/>
<xs:element name="duplicateElement" type="string"/>
<xs:complexType name="duplicateComplexType">
<xs:sequence>
<xs:element ref="duplicateElement"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
svc1.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.com/svc1" targetNamespace="http://www.example.com/svc1" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1">
<xs:element name="duplicateElement" type="xs:int"/>
<xs:complexType name="duplicateComplexType">
<xs:sequence>
<xs:element ref="duplicateElement"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
svc2.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.com/svc2" targetNamespace="http://www.example.com/svc2" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1">
<xs:element name="duplicateElement" type="xs:int"/>
<xs:complexType name="duplicateComplexType">
<xs:sequence>
<xs:element ref="duplicateElement"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Above definitions result in generated example.go:
package example
.
.
type DuplicateElement int32
type DuplicateComplexType struct {
XMLName xml.Name `xml:"http://www.example.com/svc1 duplicateComplexType"`
DuplicateElement *DuplicateElement `xml:"duplicateElement,omitempty" json:"duplicateElement,omitempty"`
}
type DuplicateElement int32
type DuplicateComplexType struct {
XMLName xml.Name `xml:"http://www.example.com/svc2 duplicateComplexType"`
DuplicateElement *DuplicateElement `xml:"duplicateElement,omitempty" json:"duplicateElement,omitempty"`
}
type DuplicateElement string
type DuplicateComplexType struct {
XMLName xml.Name `xml:"http://www.example.com/svcX duplicateComplexType"`
DuplicateElement *DuplicateElement `xml:"duplicateElement,omitempty" json:"duplicateElement,omitempty"`
}
Obviously this code does not compile due to redeclared types, i.e. naming collisions.
The scenario described here is "simple" in the sense that the types are only used within the same namespace. One could also envision such types being used from a different namespace, thus further complicating matters.
Solution Approaches
With respect to the scenario and problem described above, a few solution approaches come to mind. Approaches 1-3 attempt to change the names of duplicate types by e.g. adding some number or other namespace identifier to the name for differentiation. Approaches 1-2 have the undesired side effect that names of all types coming from different namespaces are modified (i.e. not just those colliding).
- Solve everything in the template
Line 7 in 9e1cc9a
The difficult part when only working in the template, is to make sure that all occurrences of possible name collisions are caught and that they are caught for both the type definition and all locations where those types are used. #218 proposes an approach that could help, although it "only" addresses attributes (which are a special case because they must have a namespace prefix according to specification) and it does not address name collisions.diff --git a/xsd.go b/xsd.go index 18a924b..2d05c0f 100644 --- a/xsd.go +++ b/xsd.go @@ -14,6 +15,7 @@ const xmlschema11 = "http://www.w3.org/2001/XMLSchema" type XSDSchema struct { XMLName xml.Name `xml:"schema"` Xmlns map[string]string `xml:"-"` + NsId string `xml:"-"` Tns string `xml:"xmlns tns,attr"` Xs string `xml:"xmlns xs,attr"` Version string `xml:"version,attr"`
- Change the type names in UnmarshalXML method of the XSDSchema
Line 31 in 9e1cc9a
The problem here is that sub-elements of e.g. complex types cannot be reached and modified, because they are parsed by Go's XML decoder. - Identify collisions specifically and resolve them with some logic. Possibly the traverser could be enhanced to achieve something like this. #184 proposes such an approach. However it "only" addresses complex types, while the problem as such is more generic.
- Rather than having all type definitions continue to end up in the same Go namespace (i.e. package), generating separate packages for namespaces might help somehow. I do not know all of what that would entail though.
Namespaces
I understand that part of this problem may be due to Go's limitations with XML namespace handling (the details of which I am not familiar with). The problem described above is just one particular problem with namespaces, resulting in naming collisions. But there are other problems and missing functionalities with namespaces too. I have also gone through existing issues and pull request that address similar or at least related problems:
Directly related to name collisions:
Other namespace related problems that should be considered for synergies for solution approach:
I do believe that namespace handling should be implemented in a consistent manner across all related functionalities of gowsdl. Whatever approach is chosen should make the resolution of other namespace related problems possible and easier.
Eager to hear what people think who are familiar with both Go's XML namespace support and gowsdl's implementation thus far.
Originally posted by @ieure in #218 (comment)
For #223, the issue is 100% gowsdl. It should probably put each ns' schema into its own subpackage based on its ns name, so you have
svcX.DuplicateComplexType
andsvc1.DuplicateComplexType
and the correct imports get added as needed.
I agree that this would be the correct approach if it can be made to work properly. There are a number reasons why I didn't try this yet:
- The vast majority of type names do not collide. At least for those that don't, it is more cumbersome to have them in separate packages.
- This would break backwards compatibility of gowsdl, since all definitions from separate XML namespaces would end up in new separate packages. This is relevant for scenarios where XML namespaces were used but no name collisions occurred.
- I know for instance that
dotnet-svcutil
for C# does not create C# namespaces for this case. I just resolves the naming collisions by adding numbers (1, 2, 3, ...) to those type names that would otherwise create collisions with existing names in other XML namespaces. It leaves the non-colliding type names alone at global scope. - Personal reason: my limited experience with Go does not (yet) allow me to anticipate other problems that this approach might cause.