diff --git a/fixtures/epcis/epcisquery.src b/fixtures/epcis/epcisquery.src index 0cd5f78c..05d0dc5f 100644 --- a/fixtures/epcis/epcisquery.src +++ b/fixtures/epcis/epcisquery.src @@ -27,13 +27,13 @@ type Document struct { // The version of the schema corresponding to which the instance conforms. // - SchemaVersion float64 `xml:"urn:epcglobal:xsd:1 schemaVersion,attr,omitempty" json:"schemaVersion,omitempty"` + SchemaVersion float64 `xml:"schemaVersion,attr,omitempty" json:"schemaVersion,omitempty"` // // The date the message was created. Used for auditing and logging. // - CreationDate soap.XSDDateTime `xml:"urn:epcglobal:xsd:1 creationDate,attr,omitempty" json:"creationDate,omitempty"` + CreationDate soap.XSDDateTime `xml:"creationDate,attr,omitempty" json:"creationDate,omitempty"` } type EPC string @@ -59,6 +59,8 @@ type Partner struct { } type PartnerIdentification struct { + XMLName xml.Name `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader Identifier"` + Value string `xml:",chardata" json:"-,"` Authority string `xml:"Authority,attr,omitempty" json:"Authority,omitempty"` @@ -136,25 +138,25 @@ type BusinessService struct { } type ServiceTransaction struct { - TypeOfServiceTransaction *TypeOfServiceTransaction `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader TypeOfServiceTransaction,attr,omitempty" json:"TypeOfServiceTransaction,omitempty"` + TypeOfServiceTransaction *TypeOfServiceTransaction `xml:"TypeOfServiceTransaction,attr,omitempty" json:"TypeOfServiceTransaction,omitempty"` - IsNonRepudiationRequired string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader IsNonRepudiationRequired,attr,omitempty" json:"IsNonRepudiationRequired,omitempty"` + IsNonRepudiationRequired string `xml:"IsNonRepudiationRequired,attr,omitempty" json:"IsNonRepudiationRequired,omitempty"` - IsAuthenticationRequired string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader IsAuthenticationRequired,attr,omitempty" json:"IsAuthenticationRequired,omitempty"` + IsAuthenticationRequired string `xml:"IsAuthenticationRequired,attr,omitempty" json:"IsAuthenticationRequired,omitempty"` - IsNonRepudiationOfReceiptRequired string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader IsNonRepudiationOfReceiptRequired,attr,omitempty" json:"IsNonRepudiationOfReceiptRequired,omitempty"` + IsNonRepudiationOfReceiptRequired string `xml:"IsNonRepudiationOfReceiptRequired,attr,omitempty" json:"IsNonRepudiationOfReceiptRequired,omitempty"` - IsIntegrityCheckRequired string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader IsIntegrityCheckRequired,attr,omitempty" json:"IsIntegrityCheckRequired,omitempty"` + IsIntegrityCheckRequired string `xml:"IsIntegrityCheckRequired,attr,omitempty" json:"IsIntegrityCheckRequired,omitempty"` - IsApplicationErrorResponseRequested string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader IsApplicationErrorResponseRequested,attr,omitempty" json:"IsApplicationErrorResponseRequested,omitempty"` + IsApplicationErrorResponseRequested string `xml:"IsApplicationErrorResponseRequested,attr,omitempty" json:"IsApplicationErrorResponseRequested,omitempty"` - TimeToAcknowledgeReceipt string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader TimeToAcknowledgeReceipt,attr,omitempty" json:"TimeToAcknowledgeReceipt,omitempty"` + TimeToAcknowledgeReceipt string `xml:"TimeToAcknowledgeReceipt,attr,omitempty" json:"TimeToAcknowledgeReceipt,omitempty"` - TimeToAcknowledgeAcceptance string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader TimeToAcknowledgeAcceptance,attr,omitempty" json:"TimeToAcknowledgeAcceptance,omitempty"` + TimeToAcknowledgeAcceptance string `xml:"TimeToAcknowledgeAcceptance,attr,omitempty" json:"TimeToAcknowledgeAcceptance,omitempty"` - TimeToPerform string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader TimeToPerform,attr,omitempty" json:"TimeToPerform,omitempty"` + TimeToPerform string `xml:"TimeToPerform,attr,omitempty" json:"TimeToPerform,omitempty"` - Recurrence string `xml:"http://www.unece.org/cefact/namespaces/StandardBusinessDocumentHeader Recurrence,attr,omitempty" json:"Recurrence,omitempty"` + Recurrence string `xml:"Recurrence,attr,omitempty" json:"Recurrence,omitempty"` } type StandardBusinessDocumentHeader struct { @@ -230,13 +232,13 @@ type EPCISDocumentType struct { } type EPCISDocumentExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type EPCISHeaderType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 EPCISHeader"` + XMLName xml.Name `xml:"EPCISHeader"` StandardBusinessDocumentHeader *StandardBusinessDocumentHeader `xml:"StandardBusinessDocumentHeader,omitempty" json:"StandardBusinessDocumentHeader,omitempty"` @@ -246,7 +248,7 @@ type EPCISHeaderType struct { } type EPCISHeaderExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` EPCISMasterData *EPCISMasterDataType `xml:"EPCISMasterData,omitempty" json:"EPCISMasterData,omitempty"` @@ -254,13 +256,13 @@ type EPCISHeaderExtensionType struct { } type EPCISHeaderExtension2Type struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type EPCISMasterDataType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 EPCISMasterData"` + XMLName xml.Name `xml:"EPCISMasterData"` VocabularyList *VocabularyListType `xml:"VocabularyList,omitempty" json:"VocabularyList,omitempty"` @@ -268,19 +270,19 @@ type EPCISMasterDataType struct { } type EPCISMasterDataExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type VocabularyListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 VocabularyList"` + XMLName xml.Name `xml:"VocabularyList"` Vocabulary []*VocabularyType `xml:"Vocabulary,omitempty" json:"Vocabulary,omitempty"` } type VocabularyType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 Vocabulary"` + XMLName xml.Name `xml:"Vocabulary"` VocabularyElementList *VocabularyElementListType `xml:"VocabularyElementList,omitempty" json:"VocabularyElementList,omitempty"` @@ -288,17 +290,17 @@ type VocabularyType struct { Items []string `xml:",any" json:"items,omitempty"` - Type AnyURI `xml:"urn:epcglobal:epcis:xsd:1 type,attr,omitempty" json:"type,omitempty"` + Type AnyURI `xml:"type,attr,omitempty" json:"type,omitempty"` } type VocabularyElementListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 VocabularyElementList"` + XMLName xml.Name `xml:"VocabularyElementList"` VocabularyElement []*VocabularyElementType `xml:"VocabularyElement,omitempty" json:"VocabularyElement,omitempty"` } type VocabularyElementType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 VocabularyElement"` + XMLName xml.Name `xml:"VocabularyElement"` Attribute []*AttributeType `xml:"attribute,omitempty" json:"attribute,omitempty"` @@ -308,37 +310,37 @@ type VocabularyElementType struct { Items []string `xml:",any" json:"items,omitempty"` - Id AnyURI `xml:"urn:epcglobal:epcis:xsd:1 id,attr,omitempty" json:"id,omitempty"` + Id AnyURI `xml:"id,attr,omitempty" json:"id,omitempty"` } type AttributeType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 attribute"` + XMLName xml.Name `xml:"attribute"` AnyType - Id AnyURI `xml:"urn:epcglobal:epcis:xsd:1 id,attr,omitempty" json:"id,omitempty"` + Id AnyURI `xml:"id,attr,omitempty" json:"id,omitempty"` } type IDListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 children"` + XMLName xml.Name `xml:"children"` Id []AnyURI `xml:"id,omitempty" json:"id,omitempty"` } type VocabularyExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type VocabularyElementExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type EPCISBodyType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 EPCISBody"` + XMLName xml.Name `xml:"EPCISBody"` EventList *EventListType `xml:"EventList,omitempty" json:"EventList,omitempty"` @@ -348,13 +350,13 @@ type EPCISBodyType struct { } type EPCISBodyExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type EventListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 EventList"` + XMLName xml.Name `xml:"EventList"` ObjectEvent []*ObjectEventType `xml:"ObjectEvent,omitempty" json:"ObjectEvent,omitempty"` @@ -368,7 +370,7 @@ type EventListType struct { } type EPCISEventListExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` TransformationEvent *TransformationEventType `xml:"TransformationEvent,omitempty" json:"TransformationEvent,omitempty"` @@ -376,7 +378,7 @@ type EPCISEventListExtensionType struct { } type EPCISEventListExtension2Type struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } @@ -386,7 +388,7 @@ type EPCListType struct { } type QuantityElementType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 quantityElement"` + XMLName xml.Name `xml:"quantityElement"` EpcClass *EPCClassType `xml:"epcClass,omitempty" json:"epcClass,omitempty"` } @@ -396,7 +398,7 @@ type QuantityListType struct { } type ReadPointType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 readPoint"` + XMLName xml.Name `xml:"readPoint"` Id *ReadPointIDType `xml:"id,omitempty" json:"id,omitempty"` @@ -406,13 +408,13 @@ type ReadPointType struct { } type ReadPointExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type BusinessLocationType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 bizLocation"` + XMLName xml.Name `xml:"bizLocation"` Id *BusinessLocationIDType `xml:"id,omitempty" json:"id,omitempty"` @@ -422,21 +424,21 @@ type BusinessLocationType struct { } type BusinessLocationExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type BusinessTransactionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 bizTransaction"` + XMLName xml.Name `xml:"bizTransaction"` Value *BusinessTransactionIDType `xml:",chardata" json:"-,"` - Type *BusinessTransactionTypeIDType `xml:"urn:epcglobal:epcis:xsd:1 type,attr,omitempty" json:"type,omitempty"` + Type *BusinessTransactionTypeIDType `xml:"type,attr,omitempty" json:"type,omitempty"` } type BusinessTransactionListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 bizTransactionList"` + XMLName xml.Name `xml:"bizTransactionList"` BizTransaction []*BusinessTransactionType `xml:"bizTransaction,omitempty" json:"bizTransaction,omitempty"` } @@ -444,23 +446,23 @@ type BusinessTransactionListType struct { type SourceDestType struct { Value *SourceDestIDType `xml:",chardata" json:"-,"` - Type *SourceDestTypeIDType `xml:"urn:epcglobal:epcis:xsd:1 type,attr,omitempty" json:"type,omitempty"` + Type *SourceDestTypeIDType `xml:"type,attr,omitempty" json:"type,omitempty"` } type SourceListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 sourceList"` + XMLName xml.Name `xml:"sourceList"` Source []*SourceDestType `xml:"source,omitempty" json:"source,omitempty"` } type DestinationListType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 destinationList"` + XMLName xml.Name `xml:"destinationList"` Destination []*SourceDestType `xml:"destination,omitempty" json:"destination,omitempty"` } type ILMDType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 ilmd"` + XMLName xml.Name `xml:"ilmd"` Extension *ILMDExtensionType `xml:"extension,omitempty" json:"extension,omitempty"` @@ -468,19 +470,19 @@ type ILMDType struct { } type ILMDExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type CorrectiveEventIDsType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 correctiveEventIDs"` + XMLName xml.Name `xml:"correctiveEventIDs"` CorrectiveEventID []*EventIDType `xml:"correctiveEventID,omitempty" json:"correctiveEventID,omitempty"` } type ErrorDeclarationType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 errorDeclaration"` + XMLName xml.Name `xml:"errorDeclaration"` DeclarationTime soap.XSDDateTime `xml:"declarationTime,omitempty" json:"declarationTime,omitempty"` @@ -494,7 +496,7 @@ type ErrorDeclarationType struct { } type ErrorDeclarationExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } @@ -510,7 +512,7 @@ type EPCISEventType struct { } type EPCISEventExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 baseExtension"` + XMLName xml.Name `xml:"baseExtension"` EventID *EventIDType `xml:"eventID,omitempty" json:"eventID,omitempty"` @@ -520,13 +522,13 @@ type EPCISEventExtensionType struct { } type EPCISEventExtension2Type struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type ObjectEventType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 ObjectEvent"` + XMLName xml.Name `xml:"ObjectEvent"` *EPCISEventType @@ -548,7 +550,7 @@ type ObjectEventType struct { } type ObjectEventExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` QuantityList *QuantityListType `xml:"quantityList,omitempty" json:"quantityList,omitempty"` @@ -562,13 +564,13 @@ type ObjectEventExtensionType struct { } type ObjectEventExtension2Type struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type AggregationEventType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 AggregationEvent"` + XMLName xml.Name `xml:"AggregationEvent"` *EPCISEventType @@ -592,7 +594,7 @@ type AggregationEventType struct { } type AggregationEventExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` ChildQuantityList *QuantityListType `xml:"childQuantityList,omitempty" json:"childQuantityList,omitempty"` @@ -604,13 +606,13 @@ type AggregationEventExtensionType struct { } type AggregationEventExtension2Type struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type QuantityEventType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 QuantityEvent"` + XMLName xml.Name `xml:"QuantityEvent"` *EPCISEventType @@ -632,13 +634,13 @@ type QuantityEventType struct { } type QuantityEventExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type TransactionEventType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 TransactionEvent"` + XMLName xml.Name `xml:"TransactionEvent"` *EPCISEventType @@ -662,7 +664,7 @@ type TransactionEventType struct { } type TransactionEventExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` QuantityList *QuantityListType `xml:"quantityList,omitempty" json:"quantityList,omitempty"` @@ -674,13 +676,13 @@ type TransactionEventExtensionType struct { } type TransactionEventExtension2Type struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type TransformationEventType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 TransformationEvent"` + XMLName xml.Name `xml:"TransformationEvent"` *EPCISEventType @@ -714,7 +716,7 @@ type TransformationEventType struct { } type TransformationEventExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } @@ -760,13 +762,13 @@ type EPCISQueryDocumentType struct { } type EPCISQueryDocumentExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type EPCISQueryBodyType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 EPCISBody"` + XMLName xml.Name `xml:"EPCISBody"` GetQueryNames *GetQueryNames `xml:"GetQueryNames,omitempty" json:"GetQueryNames,omitempty"` @@ -860,7 +862,7 @@ type ArrayOfString struct { } type SubscriptionControls struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 controls"` + XMLName xml.Name `xml:"controls"` Schedule *QuerySchedule `xml:"schedule,omitempty" json:"schedule,omitempty"` @@ -876,13 +878,13 @@ type SubscriptionControls struct { } type SubscriptionControlsExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type QuerySchedule struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 schedule"` + XMLName xml.Name `xml:"schedule"` Second string `xml:"second,omitempty" json:"second,omitempty"` @@ -902,19 +904,19 @@ type QuerySchedule struct { } type QueryScheduleExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type QueryParams struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 params"` + XMLName xml.Name `xml:"params"` Param []*QueryParam `xml:"param,omitempty" json:"param,omitempty"` } type QueryParam struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 param"` + XMLName xml.Name `xml:"param"` Name string `xml:"name,omitempty" json:"name,omitempty"` @@ -934,13 +936,13 @@ type QueryResults struct { } type QueryResultsExtensionType struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 extension"` + XMLName xml.Name `xml:"extension"` Items []string `xml:",any" json:"items,omitempty"` } type QueryResultsBody struct { - XMLName xml.Name `xml:"urn:epcglobal:epcis-query:xsd:1 resultsBody"` + XMLName xml.Name `xml:"resultsBody"` EventList *EventListType `xml:"EventList,omitempty" json:"EventList,omitempty"` diff --git a/gowsdl.go b/gowsdl.go index f3410986..20e6590f 100644 --- a/gowsdl.go +++ b/gowsdl.go @@ -10,7 +10,7 @@ import ( "encoding/xml" "errors" "fmt" - "io/ioutil" + "io" "log" "net" "net/http" @@ -36,18 +36,20 @@ type GoWSDL struct { wsdl *WSDL resolvedXSDExternals map[string]bool currentRecursionLevel uint8 - currentNamespace string + currentSchema *XSDSchema + typeResolver *typeResolver } -// Method setNS sets (and returns) the currently active XML namespace. -func (g *GoWSDL) setNS(ns string) string { - g.currentNamespace = ns - return ns +// setCurrentSchema sets (and returns) the currently active XSD schema. +// This may be consumed from within templates in order to help resolve namespaces. +func (g *GoWSDL) setCurrentSchema(s *XSDSchema) *XSDSchema { + g.currentSchema = s + return s } -// Method setNS returns the currently active XML namespace. -func (g *GoWSDL) getNS() string { - return g.currentNamespace +// getCurrentSchema returns the currently active XSD schema. +func (g *GoWSDL) getCurrentSchema() *XSDSchema { + return g.currentSchema } var cacheDir = filepath.Join(os.TempDir(), "gowsdl-cache") @@ -82,10 +84,10 @@ func downloadFile(url string, ignoreTLS bool) ([]byte, error) { defer resp.Body.Close() if resp.StatusCode != 200 { - return nil, fmt.Errorf("Received response code %d", resp.StatusCode) + return nil, fmt.Errorf("received response code %d while fetching %s", resp.StatusCode, url) } - data, err := ioutil.ReadAll(resp.Body) + data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -132,9 +134,10 @@ func (g *GoWSDL) Start() (map[string][]byte, error) { return nil, err } - // Process WSDL nodes - for _, schema := range g.wsdl.Types.Schemas { - newTraverser(schema, g.wsdl.Types.Schemas).traverse() + g.typeResolver = newTypeResolver(g.wsdl.Types.Schemas) + err = resolveAttrRefs(g.wsdl.Types.Schemas) + if err != nil { + return nil, fmt.Errorf("failed to resolve attribute references: %w", err) } var wg sync.WaitGroup @@ -192,7 +195,7 @@ func (g *GoWSDL) Start() (map[string][]byte, error) { func (g *GoWSDL) fetchFile(loc *Location) (data []byte, err error) { if loc.f != "" { log.Println("Reading", "file", loc.f) - data, err = ioutil.ReadFile(loc.f) + data, err = os.ReadFile(loc.f) } else { log.Println("Downloading", "file", loc.u.String()) data, err = downloadFile(loc.u.String(), g.ignoreTLS) @@ -298,10 +301,12 @@ func (g *GoWSDL) genTypes() ([]byte, error) { "comment": comment, "removeNS": removeNS, "goString": goString, - "findNameByType": g.findNameByType, + "xmlNameForType": g.xmlNameForType, "removePointerFromType": removePointerFromType, - "setNS": g.setNS, - "getNS": g.getNS, + "setCurrentSchema": g.setCurrentSchema, + "getCurrentSchema": g.getCurrentSchema, + "renderXMLName": renderXMLName, + "renderXMLTag": renderXMLTag, } data := new(bytes.Buffer) @@ -610,8 +615,8 @@ func (g *GoWSDL) findType(message string) string { } // Given a type, check if there's an Element with that type, and return its name. -func (g *GoWSDL) findNameByType(name string) string { - return newTraverser(nil, g.wsdl.Types.Schemas).findNameByType(name) +func (g *GoWSDL) xmlNameForType(typeName string, schema *XSDSchema) xml.Name { + return g.typeResolver.xmlNameForType(typeName, schema) } // TODO(c4milo): Add support for namespaces instead of striping them out @@ -731,3 +736,15 @@ func comment(text string) string { } return "" } + +func renderXMLName(xn xml.Name) string { + result := xn.Local + if xn.Space != "" { + result = fmt.Sprintf("%s %s", xn.Space, xn.Local) + } + return result +} + +func renderXMLTag(xn xml.Name) string { + return fmt.Sprintf("`xml:\"%s\"`", renderXMLName(xn)) +} diff --git a/gowsdl_test.go b/gowsdl_test.go index 639d0edf..b2883538 100644 --- a/gowsdl_test.go +++ b/gowsdl_test.go @@ -13,7 +13,6 @@ import ( "go/parser" "go/printer" "go/token" - "io/ioutil" "log" "os" "path/filepath" @@ -84,7 +83,7 @@ func TestAttributeRef(t *testing.T) { Status []struct { Value string ` + "`" + `xml:",chardata" json:"-,"` + "`" + ` - Code string ` + "`" + `xml:"http://www.mnb.hu/webservices/ code,attr,omitempty" json:"code,omitempty"` + "`" + ` + Code string ` + "`" + `xml:"code,attr,omitempty" json:"code,omitempty"` + "`" + ` } ` + "`" + `xml:"status,omitempty" json:"status,omitempty"` + "`" + ` ResponseCode string ` + "`" + `xml:"http://www.mnb.hu/webservices/ responseCode,attr,omitempty" json:"responseCode,omitempty"` + "`" + ` @@ -209,28 +208,34 @@ func TestVboxGeneratesWithoutSyntaxErrors(t *testing.T) { } for _, file := range files { - g, err := NewGoWSDL(file, "myservice", false, true) - if err != nil { - t.Error(err) - } - - resp, err := g.Start() - if err != nil { + if file == "fixtures/vim.wsdl" { + // This fixture references missing .xsd files from the VMWare SDK, which aren't actually + // vendored. We use it explicitly from test_wsdl.go to test unmarshalling, but not here. continue - //t.Error(err) } + t.Run(file, func(t *testing.T) { + g, err := NewGoWSDL(file, "myservice", false, true) + if err != nil { + t.Error(err) + } - data := new(bytes.Buffer) - data.Write(resp["header"]) - data.Write(resp["types"]) - data.Write(resp["operations"]) - data.Write(resp["soap"]) + resp, err := g.Start() + if err != nil { + t.Error(err) + } - _, err = format.Source(data.Bytes()) - if err != nil { - fmt.Println(string(data.Bytes())) - t.Error(err) - } + data := new(bytes.Buffer) + data.Write(resp["header"]) + data.Write(resp["types"]) + data.Write(resp["operations"]) + data.Write(resp["soap"]) + + _, err = format.Source(data.Bytes()) + if err != nil { + fmt.Println(data.String()) + t.Error(err) + } + }) } } @@ -308,7 +313,7 @@ func TestEPCISWSDL(t *testing.T) { if err != nil { t.Fatal(err) } - expectedBytes, err := ioutil.ReadFile("./fixtures/epcis/epcisquery.src") + expectedBytes, err := os.ReadFile("./fixtures/epcis/epcisquery.src") if err != nil { t.Fatal(err) } @@ -316,7 +321,7 @@ func TestEPCISWSDL(t *testing.T) { actual := string(source) expected := string(expectedBytes) if actual != expected { - _ = ioutil.WriteFile("./fixtures/epcis/epcisquery_gen.src", source, 0664) + _ = os.WriteFile("./fixtures/epcis/epcisquery_gen.src", source, 0664) t.Error("got source ./fixtures/epcis/epcisquery_gen.src but expected ./fixtures/epcis/epcisquery.src") } } diff --git a/resolve_attr_refs.go b/resolve_attr_refs.go new file mode 100644 index 00000000..806f8ca9 --- /dev/null +++ b/resolve_attr_refs.go @@ -0,0 +1,67 @@ +package gowsdl + +import ( + "errors" + "fmt" + "strings" +) + +// resolveAttrRefs resolves all attribute references across the provided schemas. +// It modifies the schemas in-place, copying properties from the referenced attributes +// onto the referencing attributes. +func resolveAttrRefs(schemas []*XSDSchema) error { + // First, build an index from (namespace, attrName) -> attrDef + // for all global attrs across all schemas. + attrIndex := make(map[namespacedKey]*XSDAttribute) + for _, s := range schemas { + for _, attr := range s.Attributes { + if attr.Name != "" { + attrIndex[newNamespacedKey(s.TargetNamespace, attr.Name)] = attr + } + } + } + + keyFromAttrRef := func(s *XSDSchema, attrRef string) (namespacedKey, error) { + before, after, hadColon := strings.Cut(attrRef, ":") + if !hadColon { + return newNamespacedKey(s.TargetNamespace, before), nil + } + if ns, ok := s.Xmlns[before]; ok { + return newNamespacedKey(ns, after), nil + } + return "", fmt.Errorf("unable to resolve namespace prefix %q in attribute ref %q", before, attrRef) + } + + // Next, traverse all attrs with refs and copy over the properties from the referenced attrs. + var currentSchema *XSDSchema + var errs []error + visitor{schemas}.visit(&visitorConfig{ + onEnterSchema: func(s *XSDSchema) { + currentSchema = s + }, + onEnterAttribute: func(attr *XSDAttribute) { + if attr.Ref != "" { + nsk, err := keyFromAttrRef(currentSchema, attr.Ref) + if err != nil { + errs = append(errs, err) + return + } + refAttr, ok := attrIndex[nsk] + if !ok || refAttr.Ref != "" { + errs = append(errs, fmt.Errorf("unable to resolve attribute ref %q in schema with namespace %q", attr.Ref, currentSchema.TargetNamespace)) + return + } + attr.Name = refAttr.Name + attr.Type = refAttr.Type + if attr.Fixed == "" { + attr.Fixed = refAttr.Fixed + } + attr.TargetNamespace = currentSchema.XMLNameForAttribute(refAttr).Space + } else if attr.Type == "" && attr.SimpleType != nil { + attr.Type = attr.SimpleType.Restriction.Base + } + }, + }) + + return errors.Join(errs...) +} diff --git a/soap/MMAEncoder.go b/soap/MMAEncoder.go index 42579e20..6b8de826 100644 --- a/soap/MMAEncoder.go +++ b/soap/MMAEncoder.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime" "mime/multipart" "net/textproto" @@ -130,7 +129,7 @@ func (d *mmaDecoder) Decode(v interface{}) error { if contentID == "" { return errors.New("Invalid multipart content ID") } - content, err := ioutil.ReadAll(p) + content, err := io.ReadAll(p) if err != nil { return err } diff --git a/soap/MTOMEncoder.go b/soap/MTOMEncoder.go index 640073e3..22e61398 100644 --- a/soap/MTOMEncoder.go +++ b/soap/MTOMEncoder.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math/rand" "mime" "mime/multipart" @@ -253,7 +252,7 @@ func (d *mtomDecoder) Decode(v interface{}) error { if contentID == "" { return errors.New("Invalid multipart content ID") } - content, err := ioutil.ReadAll(p) + content, err := io.ReadAll(p) if err != nil { return err } diff --git a/soap/soap.go b/soap/soap.go index 6407e027..11d20624 100644 --- a/soap/soap.go +++ b/soap/soap.go @@ -7,7 +7,6 @@ import ( "encoding/xml" "fmt" "io" - "io/ioutil" "net" "net/http" "time" @@ -503,7 +502,7 @@ func (s *Client) call(ctx context.Context, soapAction string, request, response defer res.Body.Close() if res.StatusCode >= 400 && res.StatusCode != 500 { - body, _ := ioutil.ReadAll(res.Body) + body, _ := io.ReadAll(res.Body) return &HTTPError{ StatusCode: res.StatusCode, ResponseBody: body, @@ -526,7 +525,7 @@ func (s *Client) call(ctx context.Context, soapAction string, request, response } var mmaBoundary string - if s.opts.mma{ + if s.opts.mma { mmaBoundary, err = getMmaHeader(res.Header.Get("Content-Type")) if err != nil { return err diff --git a/soap/soap_test.go b/soap/soap_test.go index b436df54..9f84f72f 100644 --- a/soap/soap_test.go +++ b/soap/soap_test.go @@ -5,7 +5,7 @@ import ( "context" "encoding/xml" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "strings" @@ -138,7 +138,7 @@ func TestClient_Attachments_WithAttachmentResponse(t *testing.T) { for k, v := range r.Header { w.Header().Set(k, v[0]) } - bodyBuf, _ := ioutil.ReadAll(r.Body) + bodyBuf, _ := io.ReadAll(r.Body) _, err := w.Write(bodyBuf) if err != nil { panic(err) @@ -183,7 +183,7 @@ func TestClient_MTOM(t *testing.T) { for k, v := range r.Header { w.Header().Set(k, v[0]) } - bodyBuf, _ := ioutil.ReadAll(r.Body) + bodyBuf, _ := io.ReadAll(r.Body) w.Write(bodyBuf) })) defer ts.Close() diff --git a/traverser.go b/traverser.go deleted file mode 100644 index 64986909..00000000 --- a/traverser.go +++ /dev/null @@ -1,202 +0,0 @@ -package gowsdl - -import ( - "encoding/xml" - "strings" -) - -type traverseMode int32 - -const ( - refResolution traverseMode = iota - findNameByType -) - -type traverser struct { - c *XSDSchema - all []*XSDSchema - tm traverseMode - // fields used by findNameByType mode - typeName string - foundElmName string - conflictingTypeUsage bool -} - -func newTraverser(c *XSDSchema, all []*XSDSchema) *traverser { - return &traverser{ - c: c, - all: all, - tm: refResolution, // default traverse mode is refResolution - } -} - -func (t *traverser) traverse() { - t.tm = refResolution - - for _, ct := range t.c.ComplexTypes { - t.traverseComplexType(ct) - } - for _, st := range t.c.SimpleType { - t.traverseSimpleType(st) - } - for _, elm := range t.c.Elements { - t.traverseElement(elm) - } -} - -// Given a type, check if there is an Element with that type, and return its name. -// If multiple elements with identical names of the given type are found, -// the name is returned. -// If multiple elements with different names of the given type are found, -// the original type name is returned instead. -// If no elements are found, the original type name is returned instead. -func (t *traverser) findNameByType(name string) string { - t.initFindNameByType(name) - - // Search for elements of given type - for _, schema := range t.all { - for _, elm := range schema.Elements { - t.traverseElement(elm) - } - for _, ct := range schema.ComplexTypes { - t.traverseComplexType(ct) - } - for _, st := range schema.SimpleType { - t.traverseSimpleType(st) - } - } - - // Return found element name if given type is used only once - if len(t.foundElmName) > 0 && !t.conflictingTypeUsage { - return t.foundElmName - } - - // Return original type name - // No element found or conflicting element names found - return t.typeName -} - -func (t *traverser) initFindNameByType(name string) { - // Initialize fields for processing - t.tm = findNameByType - t.typeName = stripns(name) - t.foundElmName = "" - t.conflictingTypeUsage = false -} - -func (t *traverser) traverseElements(ct []*XSDElement) { - for _, elm := range ct { - t.traverseElement(elm) - } -} - -func (t *traverser) traverseElement(elm *XSDElement) { - t.findElmName(elm) - - if elm.ComplexType != nil { - t.traverseComplexType(elm.ComplexType) - } - if elm.SimpleType != nil { - t.traverseSimpleType(elm.SimpleType) - } -} - -func (t *traverser) findElmName(elm *XSDElement) { - // Check if we are called by findNameByType - if t.tm != findNameByType { - return - } - - // Conflicting type usage already detected -> no need to search any further - if t.conflictingTypeUsage { - return - } - - if stripns(elm.Type) == t.typeName { - if len(t.foundElmName) == 0 { - // First time usage t.typeName - t.foundElmName = elm.Name - } else if t.foundElmName != elm.Name { - // Duplicate use of t.typeName with different element names - t.conflictingTypeUsage = true - } - } -} - -func (t *traverser) traverseSimpleType(st *XSDSimpleType) { -} - -func (t *traverser) traverseComplexType(ct *XSDComplexType) { - t.traverseElements(ct.Sequence) - t.traverseElements(ct.Choice) - t.traverseElements(ct.SequenceChoice) - t.traverseElements(ct.All) - t.traverseAttributes(ct.Attributes) - t.traverseAttributes(ct.ComplexContent.Extension.Attributes) - t.traverseElements(ct.ComplexContent.Extension.Sequence) - t.traverseElements(ct.ComplexContent.Extension.Choice) - t.traverseElements(ct.ComplexContent.Extension.SequenceChoice) - t.traverseAttributes(ct.SimpleContent.Extension.Attributes) -} - -func (t *traverser) traverseAttributes(attrs []*XSDAttribute) { - for _, attr := range attrs { - t.traverseAttribute(attr) - } -} - -func (t *traverser) traverseAttribute(attr *XSDAttribute) { - // Check if we are in ref resolution mode - if t.tm != refResolution { - return - } - - if attr.Ref != "" { - refAttr := t.getGlobalAttribute(attr.Ref) - if refAttr != nil && refAttr.Ref == "" { - t.traverseAttribute(refAttr) - attr.Name = refAttr.Name - attr.Type = refAttr.Type - if attr.Fixed == "" { - attr.Fixed = refAttr.Fixed - } - } - } else if attr.Type == "" { - if attr.SimpleType != nil { - t.traverseSimpleType(attr.SimpleType) - attr.Type = attr.SimpleType.Restriction.Base - } - } -} - -func (t *traverser) getGlobalAttribute(name string) *XSDAttribute { - ref := t.qname(name) - - for _, schema := range t.all { - if schema.TargetNamespace == ref.Space { - for _, attr := range schema.Attributes { - if attr.Name == ref.Local { - return attr - } - } - } - } - - return nil -} - -// qname resolves QName into xml.Name. -func (t *traverser) qname(name string) (qname xml.Name) { - x := strings.SplitN(name, ":", 2) - if len(x) == 1 { - qname.Local = x[0] - } else { - qname.Local = x[1] - qname.Space = x[0] - if ns, ok := t.c.Xmlns[qname.Space]; ok { - qname.Space = ns - } - } - - return qname -} diff --git a/type_resolver.go b/type_resolver.go new file mode 100644 index 00000000..d976fb59 --- /dev/null +++ b/type_resolver.go @@ -0,0 +1,97 @@ +package gowsdl + +import ( + "encoding/xml" + "strings" +) + +type typeResolver struct { + elementsByTypeName map[namespacedKey][]elementAndSchema +} + +type namespacedKey string + +func newNamespacedKey(namespace, local string) namespacedKey { + return namespacedKey(namespace + "|" + local) +} + +type elementAndSchema struct { + element *XSDElement + schema *XSDSchema +} + +func newTypeResolver(schemas []*XSDSchema) *typeResolver { + var currentSchema *XSDSchema + elementsByTypeName := make(map[namespacedKey][]elementAndSchema) + + visitor{schemas}.visit(&visitorConfig{ + onEnterSchema: func(s *XSDSchema) { + currentSchema = s + }, + onEnterElement: func(e *XSDElement) { + if e.Type == "" { + return + } + before, after, hadColon := strings.Cut(e.Type, ":") + var key namespacedKey + if hadColon { + key = newNamespacedKey(currentSchema.Xmlns[before], after) + } else { + key = newNamespacedKey( + currentSchema.XMLNameForElement(e).Space, + e.Type, + ) + } + elementsByTypeName[key] = append(elementsByTypeName[key], elementAndSchema{ + element: e, + schema: currentSchema, + }) + }, + }) + return &typeResolver{ + elementsByTypeName: elementsByTypeName, + } +} + +// Given a type qname and a schema that it appears within, determine the XMLName +// tag that should be used on the Go type representing that XML type. +// +// If no elements with the given type exist return the original typeName plus the schema's +// target namespace. +// +// If all elements with the given type have the same name and namespace, return that name +// and namespace. +// +// Otherwise, if there are mismatched names or namespaces among the elements with the given +// type, return the original typeName plus the schema's target namespace. +// +// Note that this last case will likely result in broken generated code. +// The more correct solution here is probably to not generate XMLName fields at all, +// except for types used directly as request/response wrappers, and instead emit namespaces +// on field tags, but that would be a somewhat involved change. +func (tr *typeResolver) xmlNameForType(typeName string, schema *XSDSchema) xml.Name { + key := newNamespacedKey(schema.TargetNamespace, typeName) + elements, ok := tr.elementsByTypeName[key] + if !ok { + return xml.Name{ + Space: schema.TargetNamespace, + Local: typeName, + } + } + + elementNames := make(map[string]struct{}) + namespaces := make(map[string]struct{}) + for _, e := range elements { + elementNames[e.element.Name] = struct{}{} + namespaces[e.schema.XMLNameForElement(e.element).Space] = struct{}{} + } + if len(elementNames) == 1 && len(namespaces) == 1 { + e := elements[0] + return e.schema.XMLNameForElement(e.element) + } + + return xml.Name{ + Space: schema.TargetNamespace, + Local: typeName, + } +} diff --git a/types_tmpl.go b/types_tmpl.go index e5221d81..d0320b65 100644 --- a/types_tmpl.go +++ b/types_tmpl.go @@ -44,14 +44,17 @@ var typesTmpl = ` {{end}} {{define "Attributes"}} - {{ $targetNamespace := getNS }} + {{ $schema := getCurrentSchema }} {{range .}} + {{$normalizedName := normalize .Name | makeFieldPublic}} + {{$xmlName := $schema.XMLNameForAttribute .}} {{if .Doc}} {{.Doc | comment}} {{end}} {{ if ne .Type "" }} - {{ normalize .Name | makeFieldPublic}} {{toGoType .Type false}} ` + "`" + `xml:"{{with $targetNamespace}}{{.}} {{end}}{{.Name}},attr,omitempty" json:"{{.Name}},omitempty"` + "`" + ` + {{$normalizedName}} {{toGoType .Type false}} ` + "`" + `xml:"{{renderXMLName $xmlName}},attr,omitempty" json:"{{.Name}},omitempty"` + "`" + ` {{ else }} - {{ normalize .Name | makeFieldPublic}} string ` + "`" + `xml:"{{with $targetNamespace}}{{.}} {{end}}{{.Name}},attr,omitempty" json:"{{.Name}},omitempty"` + "`" + ` + {{$normalizedName}} string ` + "`" + `xml:"{{renderXMLName $xmlName}},attr,omitempty" json:"{{.Name}},omitempty"` + "`" + ` {{ end }} + {{end}} {{end}} @@ -108,20 +111,22 @@ var typesTmpl = ` {{end}} {{range .Schemas}} - {{ $targetNamespace := setNS .TargetNamespace }} + {{ $schema := . }} + {{ $currentSchema := setCurrentSchema . }} {{range .SimpleType}} {{template "SimpleType" .}} {{end}} {{range .Elements}} + {{$element := .}} {{$name := .Name}} {{$typeName := replaceReservedWords $name | makePublic}} {{if not .Type}} {{/* ComplexTypeLocal */}} {{with .ComplexType}} type {{$typeName}} struct { - XMLName xml.Name ` + "`xml:\"{{$targetNamespace}} {{$name}}\"`" + ` + XMLName xml.Name {{renderXMLTag ($schema.XMLNameForElement $element) }} {{if ne .ComplexContent.Extension.Base ""}} {{template "ComplexContent" .ComplexContent}} {{else if ne .SimpleContent.Extension.Base ""}} @@ -201,9 +206,12 @@ var typesTmpl = ` type {{$typeName}} string {{else}} type {{$typeName}} struct { - {{$type := findNameByType .Name}} - {{if ne .Name $type}} - XMLName xml.Name ` + "`xml:\"{{$targetNamespace}} {{$type}}\"`" + ` + {{$originalTypeName := .Name}} + {{$xmlName := xmlNameForType .Name $schema}} + {{with $xmlName}} + {{if ne .Local $originalTypeName}} + XMLName xml.Name {{renderXMLTag .}} + {{end}} {{end}} {{if ne .ComplexContent.Extension.Base ""}} diff --git a/visitor.go b/visitor.go new file mode 100644 index 00000000..89b4223d --- /dev/null +++ b/visitor.go @@ -0,0 +1,110 @@ +package gowsdl + +type visitor struct { + all []*XSDSchema +} + +type visitorConfig struct { + onEnterSchema func(*XSDSchema) + onExitSchema func(*XSDSchema) + onEnterElement func(*XSDElement) + onExitElement func(*XSDElement) + onEnterComplexType func(*XSDComplexType) + onExitComplexType func(*XSDComplexType) + onEnterSimpleType func(*XSDSimpleType) + onExitSimpleType func(*XSDSimpleType) + onEnterAttribute func(*XSDAttribute) + onExitAttribute func(*XSDAttribute) +} + +func (v visitor) visit(cfg *visitorConfig) { + for _, schema := range v.all { + if cfg.onEnterSchema != nil { + cfg.onEnterSchema(schema) + } + v.visitSchema(schema, cfg) + if cfg.onExitSchema != nil { + cfg.onExitSchema(schema) + } + } +} + +func (v visitor) visitSchema(s *XSDSchema, cfg *visitorConfig) { + for _, elm := range s.Elements { + v.visitElement(elm, cfg) + } + for _, ct := range s.ComplexTypes { + v.visitComplexType(ct, cfg) + } + for _, st := range s.SimpleType { + v.visitSimpleType(st, cfg) + } +} + +func (v visitor) visitElements(es []*XSDElement, cfg *visitorConfig) { + for _, e := range es { + v.visitElement(e, cfg) + } +} + +func (v visitor) visitAttribute(attr *XSDAttribute, cfg *visitorConfig) { + if cfg.onEnterAttribute != nil { + cfg.onEnterAttribute(attr) + } + if cfg.onExitAttribute != nil { + cfg.onExitAttribute(attr) + } +} + +func (v visitor) visitAttributes(attrs []*XSDAttribute, cfg *visitorConfig) { + for _, attr := range attrs { + v.visitAttribute(attr, cfg) + } +} + +func (v visitor) visitElement(e *XSDElement, cfg *visitorConfig) { + if cfg.onEnterElement != nil { + cfg.onEnterElement(e) + } + + if e.ComplexType != nil { + v.visitComplexType(e.ComplexType, cfg) + } + if e.SimpleType != nil { + v.visitSimpleType(e.SimpleType, cfg) + } + + if cfg.onExitElement != nil { + cfg.onExitElement(e) + } +} + +func (v visitor) visitComplexType(ct *XSDComplexType, cfg *visitorConfig) { + if cfg.onEnterComplexType != nil { + cfg.onEnterComplexType(ct) + } + + v.visitElements(ct.Sequence, cfg) + v.visitElements(ct.Choice, cfg) + v.visitElements(ct.SequenceChoice, cfg) + v.visitElements(ct.All, cfg) + v.visitAttributes(ct.Attributes, cfg) + v.visitAttributes(ct.ComplexContent.Extension.Attributes, cfg) + v.visitElements(ct.ComplexContent.Extension.Sequence, cfg) + v.visitElements(ct.ComplexContent.Extension.Choice, cfg) + v.visitElements(ct.ComplexContent.Extension.SequenceChoice, cfg) + v.visitAttributes(ct.SimpleContent.Extension.Attributes, cfg) + + if cfg.onExitComplexType != nil { + cfg.onExitComplexType(ct) + } +} + +func (v visitor) visitSimpleType(st *XSDSimpleType, cfg *visitorConfig) { + if cfg.onEnterSimpleType != nil { + cfg.onEnterSimpleType(st) + } + if cfg.onExitSimpleType != nil { + cfg.onExitSimpleType(st) + } +} diff --git a/wsdl_test.go b/wsdl_test.go index 41eb7fd8..7d8e4c76 100644 --- a/wsdl_test.go +++ b/wsdl_test.go @@ -6,12 +6,12 @@ package gowsdl import ( "encoding/xml" - "io/ioutil" + "os" "testing" ) func TestUnmarshal(t *testing.T) { - data, err := ioutil.ReadFile("fixtures/vim.wsdl") + data, err := os.ReadFile("fixtures/vim.wsdl") if err != nil { t.Errorf("incorrect result\ngot: %#v\nwant: %#v", err, nil) } diff --git a/xsd.go b/xsd.go index 44c5b35a..3d7d4b7e 100644 --- a/xsd.go +++ b/xsd.go @@ -12,19 +12,70 @@ const xmlschema11 = "http://www.w3.org/2001/XMLSchema" // XSDSchema represents an entire Schema structure. type XSDSchema struct { - XMLName xml.Name `xml:"schema"` - Xmlns map[string]string `xml:"-"` - Tns string `xml:"xmlns tns,attr"` - Xs string `xml:"xmlns xs,attr"` - Version string `xml:"version,attr"` - TargetNamespace string `xml:"targetNamespace,attr"` - ElementFormDefault string `xml:"elementFormDefault,attr"` - Includes []*XSDInclude `xml:"include"` - Imports []*XSDImport `xml:"import"` - Elements []*XSDElement `xml:"element"` - Attributes []*XSDAttribute `xml:"attribute"` - ComplexTypes []*XSDComplexType `xml:"complexType"` // global - SimpleType []*XSDSimpleType `xml:"simpleType"` + XMLName xml.Name `xml:"schema"` + Xmlns map[string]string `xml:"-"` + Tns string `xml:"xmlns tns,attr"` + Xs string `xml:"xmlns xs,attr"` + Version string `xml:"version,attr"` + TargetNamespace string `xml:"targetNamespace,attr"` + ElementFormDefault string `xml:"elementFormDefault,attr"` + AttributeFormDefault string `xml:"attributeFormDefault,attr"` + Includes []*XSDInclude `xml:"include"` + Imports []*XSDImport `xml:"import"` + Elements []*XSDElement `xml:"element"` + Attributes []*XSDAttribute `xml:"attribute"` + ComplexTypes []*XSDComplexType `xml:"complexType"` // global + SimpleType []*XSDSimpleType `xml:"simpleType"` +} + +// See https://www.w3.org/TR/xmlschema11-1/#declare-element +func (s *XSDSchema) XMLNameForElement(e *XSDElement) (xn xml.Name) { + var isTopLevel bool + for _, el := range s.Elements { + if el == e { + isTopLevel = true + break + } + } + + xn.Local = e.Name + if isTopLevel { + xn.Space = s.TargetNamespace + return + } + if e.TargetNamespace != "" { + xn.Space = e.TargetNamespace + return + } + if e.Form == "qualified" || (e.Form == "" && s.ElementFormDefault == "qualified") { + xn.Space = s.TargetNamespace + return + } + return xn +} + +func (s *XSDSchema) XMLNameForAttribute(attr *XSDAttribute) (xn xml.Name) { + var isTopLevel bool + for _, a := range s.Attributes { + if a == attr { + isTopLevel = true + break + } + } + xn.Local = attr.Name + if isTopLevel { + xn.Space = s.TargetNamespace + return + } + if attr.TargetNamespace != "" { + xn.Space = attr.TargetNamespace + return + } + if attr.Form == "qualified" || (attr.Form == "" && s.AttributeFormDefault == "qualified") { + xn.Space = s.TargetNamespace + return + } + return } // UnmarshalXML implements interface xml.Unmarshaler for XSDSchema. @@ -44,6 +95,8 @@ func (s *XSDSchema) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { s.TargetNamespace = attr.Value case "elementFormDefault": s.ElementFormDefault = attr.Value + case "attributeFormDefault": + s.AttributeFormDefault = attr.Value } } @@ -124,17 +177,19 @@ type XSDImport struct { // XSDElement represents a Schema element. type XSDElement struct { - XMLName xml.Name `xml:"element"` - Name string `xml:"name,attr"` - Doc string `xml:"annotation>documentation"` - Nillable bool `xml:"nillable,attr"` - Type string `xml:"type,attr"` - Ref string `xml:"ref,attr"` - MinOccurs string `xml:"minOccurs,attr"` - MaxOccurs string `xml:"maxOccurs,attr"` - ComplexType *XSDComplexType `xml:"complexType"` // local - SimpleType *XSDSimpleType `xml:"simpleType"` - Groups []*XSDGroup `xml:"group"` + XMLName xml.Name `xml:"element"` + Name string `xml:"name,attr"` + Doc string `xml:"annotation>documentation"` + Nillable bool `xml:"nillable,attr"` + Type string `xml:"type,attr"` + Ref string `xml:"ref,attr"` + MinOccurs string `xml:"minOccurs,attr"` + MaxOccurs string `xml:"maxOccurs,attr"` + ComplexType *XSDComplexType `xml:"complexType"` // local + SimpleType *XSDSimpleType `xml:"simpleType"` + Groups []*XSDGroup `xml:"group"` + TargetNamespace string `xml:"targetNamespace,attr"` + Form string `xml:"form,attr"` } // XSDAny represents a Schema element. @@ -200,13 +255,15 @@ type XSDExtension struct { // attributes. If an element has attributes, it is considered to be of a // complex type. But the attribute itself is always declared as a simple type. type XSDAttribute struct { - Doc string `xml:"annotation>documentation"` - Name string `xml:"name,attr"` - Ref string `xml:"ref,attr"` - Type string `xml:"type,attr"` - Use string `xml:"use,attr"` - Fixed string `xml:"fixed,attr"` - SimpleType *XSDSimpleType `xml:"simpleType"` + Doc string `xml:"annotation>documentation"` + Name string `xml:"name,attr"` + Ref string `xml:"ref,attr"` + Type string `xml:"type,attr"` + Use string `xml:"use,attr"` + Fixed string `xml:"fixed,attr"` + SimpleType *XSDSimpleType `xml:"simpleType"` + TargetNamespace string `xml:"targetNamespace,attr"` + Form string `xml:"form,attr"` } // XSDSimpleType element defines a simple type and specifies the constraints