├── .gitignore ├── README.md ├── sample └── server.go ├── server.go ├── soap └── soap.go ├── type.go ├── wsdl └── wsdl.go └── xsd └── xsd.go /.gitignore: -------------------------------------------------------------------------------- 1 | sample/sample -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gosoap 2 | golang webservice 3 | -------------------------------------------------------------------------------- /sample/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/afocus/gosoap" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // 简单的 11 | type User struct { 12 | In struct { 13 | Id int64 14 | } 15 | Out struct { 16 | Id int64 17 | Name string 18 | Sex int 19 | Address string 20 | Birthday time.Time 21 | } 22 | } 23 | 24 | func (u *User) Action() *gosoap.SoapFault { 25 | 26 | if u.In.Id != 100 { 27 | return gosoap.NewSoapFault("输入参数错误", "id必须是100", "") 28 | } 29 | 30 | u.Out.Id = u.In.Id 31 | u.Out.Name = "Afocus" 32 | u.Out.Birthday = time.Now() 33 | u.Out.Sex = 1 34 | u.Out.Address = "陕西西安未央区" 35 | 36 | return nil 37 | } 38 | 39 | // 返回带列表的 40 | type DataList struct { 41 | In struct { 42 | Page int 43 | PerPage int 44 | Search string 45 | } 46 | Out struct { 47 | Total int 48 | Items []struct { 49 | Id int64 50 | Name string 51 | Status int 52 | } 53 | } 54 | } 55 | 56 | func (d *DataList) Action() *gosoap.SoapFault { 57 | fmt.Printf("%+v\n", d.In) 58 | d.Out.Total = 10 59 | d.Out.Items = make([]struct { 60 | Id int64 61 | Name string 62 | Status int 63 | }, 10) 64 | for index := 0; index < 10; index++ { 65 | d.Out.Items[index].Id = int64(index) 66 | // 67 | rand.Seed(time.Now().UnixNano()) 68 | kinds := [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}} 69 | name := make([]byte, 10) 70 | for i := 0; i < 10; i++ { 71 | ikind := rand.Intn(3) 72 | scope, base := kinds[ikind][0], kinds[ikind][1] 73 | name[i] = uint8(base + rand.Intn(scope)) 74 | } 75 | 76 | d.Out.Items[index].Name = fmt.Sprintf("%s%s%d", d.In.Search, string(name), index) 77 | d.Out.Items[index].Status = 0 78 | } 79 | return nil 80 | } 81 | 82 | func main() { 83 | s := gosoap.NewServer("people") 84 | s.Register(new(User), new(DataList)) 85 | 86 | panic(s.Service("8080")) 87 | } 88 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package gosoap 2 | 3 | import ( 4 | "bytes" 5 | "encoding/xml" 6 | "errors" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "reflect" 11 | "strings" 12 | 13 | "github.com/afocus/gosoap/soap" 14 | "github.com/afocus/gosoap/wsdl" 15 | "github.com/afocus/gosoap/xsd" 16 | ) 17 | 18 | // Methoder 方法接口 19 | // 所有注册的服务方法必须实现此接口 20 | type Methoder interface { 21 | Action() *SoapFault 22 | } 23 | 24 | // BindHost 绑定的访问地址 25 | // 不带http:// 以及端口 26 | // 例如 www.baidu.com 、180.97.81.222、 webservice.demo.io 27 | var BindHost = "127.0.0.1" 28 | 29 | // Server 结构 30 | type Server struct { 31 | // 服务名称 32 | name string 33 | // 映射的方法列表 34 | methods map[string]reflect.Type 35 | // wsdl对象 36 | wsdl *wsdl.Definitions 37 | // 缓存生成的wsdl文件 38 | wsdlcache []byte 39 | // wsdl文件的自主命名空间 40 | namespace string 41 | } 42 | 43 | // SoapFault 44 | // 响应时返回的错误类型 45 | type SoapFault soap.Fault 46 | 47 | // NewSoapFault 创建一个soap错误 48 | // 主要用在Methoder接口的Action返回值 49 | func NewSoapFault(faultcode, faultstring, detail string) *SoapFault { 50 | f := new(SoapFault) 51 | f.FaultCode = faultcode 52 | f.FaultString = faultstring 53 | f.Detail = detail 54 | return f 55 | } 56 | 57 | // NewServer 创建一个服务 58 | // 创建后如果只有一个服务可以使用Server.Service监听服务 59 | func NewServer(name string) *Server { 60 | namespace := fmt.Sprintf("http://%s/%s", BindHost, name) 61 | s := &Server{ 62 | name: name, 63 | namespace: namespace, 64 | methods: make(map[string]reflect.Type), 65 | } 66 | s.buildWsdl() 67 | return s 68 | } 69 | 70 | // Register 向服务中注册方法 71 | // 可以同时注册多个服务 72 | // 多服务的监听需要调用 MulitService 73 | func (s *Server) Register(method ...Methoder) error { 74 | for _, v := range method { 75 | if erro := s.register(v); erro != nil { 76 | return erro 77 | } 78 | } 79 | return nil 80 | } 81 | 82 | // Service 单服务监听 83 | func (s *Server) Service(port string) error { 84 | if erro := s.bind(port); erro != nil { 85 | return erro 86 | } 87 | return listen(port) 88 | } 89 | 90 | // MulitService 多服务监听 91 | func MulitService(port string, server ...*Server) error { 92 | for _, v := range server { 93 | if erro := v.bind(port); erro != nil { 94 | return erro 95 | } 96 | } 97 | return listen(port) 98 | } 99 | 100 | ///////////////////////////////////////////////////////////////////////////////////////////// 101 | ///////////////////////////////////////////////////////////////////////////////////////////// 102 | ///////////////////////////////////////////////////////////////////////////////////////////// 103 | 104 | func responeHeader(w http.ResponseWriter) { 105 | w.Header().Set("Content-Type", "text/xml; charset=utf-8") 106 | w.Header().Set("Accpet", "text/xml") 107 | w.Write([]byte(xml.Header)) 108 | } 109 | 110 | // 监听服务 111 | // 这里可以设置http 比如超时时间 最大连接数等 后续再做 112 | func listen(port string) error { 113 | return http.ListenAndServe(":"+port, nil) 114 | } 115 | 116 | // 解析参数并转化为对应的wsdl message 117 | func (s *Server) parseMessage(name string, t reflect.Type, field string) error { 118 | msg := wsdl.Message{Name: name} 119 | if f, has := t.FieldByName(field); !has { 120 | return errors.New("method结构体缺少必要参数" + field) 121 | } else { 122 | retype := f.Type 123 | if retype.Kind() != reflect.Struct { 124 | return errors.New("method结构体 In,Out参数必须是结构体") 125 | } 126 | // 遍历结构体参数列表 127 | for i := 0; i < retype.NumField(); i++ { 128 | name, _ := getTagsInfo(retype.Field(i)) 129 | ik := retype.Field(i).Type.Kind() 130 | ts, erro := checkBaseTypeKind(ik) 131 | // 如果非基本类型则转为自有命名空间的自定义类型 132 | if erro != nil { 133 | ts = "tns:" + name + ik.String() 134 | } 135 | msg.Part = append(msg.Part, wsdl.Part{Name: name, Type: ts}) 136 | } 137 | s.wsdl.Message = append(s.wsdl.Message, msg) 138 | return nil 139 | } 140 | } 141 | 142 | func (s *Server) register(a Methoder) error { 143 | t := reflect.TypeOf(a) 144 | if t.Kind() == reflect.Ptr { 145 | t = t.Elem() 146 | } 147 | if t.Kind() != reflect.Struct { 148 | return errors.New("method 不是struct") 149 | } 150 | // 方法名 151 | tname := t.Name() 152 | if _, ok := s.methods[tname]; ok { 153 | return errors.New("方法重复注册:" + tname) 154 | } 155 | // message 156 | erro := s.parseMessage(tname+"Request", t, "In") 157 | if erro != nil { 158 | return erro 159 | } 160 | erro = s.parseMessage(tname+"Respone", t, "Out") 161 | if erro != nil { 162 | return erro 163 | } 164 | s.regWsdlBindPort(tname) 165 | // 方法类型映射到server的methods中 166 | // 接受处理时可以反射出In Out参数的具体值 167 | reflectVal := reflect.ValueOf(a) 168 | mt := reflect.Indirect(reflectVal).Type() 169 | s.methods[tname] = mt 170 | return nil 171 | } 172 | 173 | func (s *Server) regWsdlBindPort(name string) { 174 | // portype 175 | op := wsdl.PortTypeOperation{ 176 | Name: name, 177 | Input: wsdl.PortTypeOperationMessage{Message: "tns:" + name + "Request"}, 178 | Output: wsdl.PortTypeOperationMessage{Message: "tns:" + name + "Respone"}, 179 | } 180 | s.wsdl.PortType.Operations = append(s.wsdl.PortType.Operations, op) 181 | // binding 182 | soapio := wsdl.SoapBodyIO{SoapBody: wsdl.SoapBody{Use: "encoded"}} 183 | bindop := wsdl.BindingOperation{ 184 | Name: name, Input: soapio, 185 | Output: soapio, 186 | SoapOperation: wsdl.SoapOperation{ 187 | Style: "rpc", SoapAction: s.wsdl.Tns + "/" + name, 188 | }, 189 | } 190 | s.wsdl.Binding.Operations = append(s.wsdl.Binding.Operations, bindop) 191 | } 192 | 193 | func (s *Server) handleFunc(w http.ResponseWriter, r *http.Request) { 194 | if r.Method != "POST" { 195 | // 网址带参数wsdl则显示wsdl文件 196 | if strings.EqualFold("wsdl", r.URL.RawQuery) { 197 | responeHeader(w) 198 | w.Write(s.wsdlcache) 199 | } else { 200 | // 其他情况返回一个提示信息 201 | w.Write([]byte("welcome")) 202 | } 203 | return 204 | } 205 | // post请求则处理接受的xml 206 | // 读取post的body信息 207 | b, erro := ioutil.ReadAll(r.Body) 208 | if erro != nil { 209 | serverSoapFault( 210 | w, NewSoapFault("Server", "读取body出错", erro.Error()), 211 | ) 212 | return 213 | } 214 | defer r.Body.Close() 215 | 216 | // 转化为Envelope对象 217 | env := soap.Envelope{} 218 | xml.Unmarshal(b, &env) 219 | // 解析请求的方法名字 220 | var startEle *xml.StartElement 221 | reader := bytes.NewReader(env.Body.Content) 222 | de := xml.NewDecoder(reader) 223 | for { 224 | t, erro := de.Token() 225 | if erro != nil { 226 | break 227 | } 228 | if x, ok := t.(xml.StartElement); ok { 229 | startEle = &x 230 | break 231 | } 232 | } 233 | if startEle == nil { 234 | serverSoapFault( 235 | w, NewSoapFault("Server", "接受到的data无效", ""), 236 | ) 237 | return 238 | } 239 | s.request(w, de, startEle) 240 | } 241 | 242 | func serverSoapFault(w http.ResponseWriter, fault *SoapFault) { 243 | responeHeader(w) 244 | data, _ := xml.Marshal(fault) 245 | b, _ := xml.Marshal(soap.NewEnvelope(data)) 246 | w.Write(b) 247 | } 248 | 249 | func (s *Server) request(w http.ResponseWriter, de *xml.Decoder, startEle *xml.StartElement) { 250 | 251 | mname := startEle.Name.Local 252 | t, has := s.methods[mname] 253 | if !has { 254 | serverSoapFault( 255 | w, NewSoapFault("Server", "没有这个方法:"+mname, ""), 256 | ) 257 | return 258 | } 259 | v := reflect.New(t) 260 | // 解析入参 261 | params := v.Elem().FieldByName("In").Addr().Interface() 262 | erro := de.DecodeElement(params, startEle) 263 | if erro != nil { 264 | serverSoapFault( 265 | w, NewSoapFault("Client", "接受参数错误", erro.Error()), 266 | ) 267 | return 268 | } 269 | // 调用action方法 270 | rets := v.MethodByName("Action").Call([]reflect.Value{}) 271 | // 处理返回值 272 | fault := rets[0].Interface().(*SoapFault) 273 | if fault != nil { 274 | serverSoapFault(w, fault) 275 | return 276 | } 277 | // 解析Out出参 278 | name := v.Elem().Type().Name() + "Respone" 279 | returns := v.Elem().FieldByName("Out").Interface() 280 | buf := bytes.NewBuffer([]byte{}) 281 | en := xml.NewEncoder(buf) 282 | // 283 | soapbody := xml.StartElement{ 284 | Name: xml.Name{Local: name}, 285 | Attr: []xml.Attr{{ 286 | Name: xml.Name{Local: "xmlns"}, 287 | Value: s.namespace, 288 | }}, 289 | } 290 | erro = en.EncodeElement(returns, soapbody) 291 | if erro != nil { 292 | serverSoapFault( 293 | w, NewSoapFault("Server", "Respone错误", erro.Error()), 294 | ) 295 | return 296 | } 297 | responeHeader(w) 298 | b, _ := xml.Marshal(soap.NewEnvelope(buf.Bytes())) 299 | w.Write(b) 300 | 301 | } 302 | 303 | func (s *Server) bind(port string) error { 304 | // 本服务的绑定地址 305 | adr := fmt.Sprintf("http://%s:%s/%s", BindHost, port, s.name) 306 | s.wsdl.Service.Port.Address.Location = adr 307 | b, erro := xml.Marshal(s.wsdl) 308 | if erro != nil { 309 | return erro 310 | } 311 | s.wsdlcache = append(s.wsdlcache, b...) 312 | http.HandleFunc("/"+s.name, s.handleFunc) 313 | return nil 314 | } 315 | 316 | // func get_internal() string { 317 | // addrs, err := net.InterfaceAddrs() 318 | // if err == nil { 319 | // for _, a := range addrs { 320 | // if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 321 | // if ipnet.IP.To4() != nil { 322 | // return ipnet.IP.String() 323 | // } 324 | // } 325 | // } 326 | // } 327 | // return "" 328 | // } 329 | 330 | func (s *Server) buildWsdl() { 331 | def := &wsdl.Definitions{ 332 | Tns: s.namespace, 333 | TargetNs: s.namespace, 334 | Soap: "http://schemas.xmlsoap.org/wsdl/soap/", 335 | SoapEnv: "http://schemas.xmlsoap.org/soap/envelope/", 336 | Wsdl: "http://schemas.xmlsoap.org/wsdl/", 337 | Xsd: "http://www.w3.org/2001/XMLSchema", 338 | Xsi: "http://www.w3.org/2001/XMLSchema-instance", 339 | } 340 | sch := xsd.Schema{ 341 | TargetNamespace: s.namespace, 342 | Import: []xsd.Import{ 343 | {Namespace: "http://schemas.xmlsoap.org/soap/encoding/"}, 344 | {Namespace: "http://schemas.xmlsoap.org/wsdl/"}}, 345 | } 346 | def.Types.Schemas = append(def.Types.Schemas, sch) 347 | 348 | def.PortType.Name = s.name + "PortType" 349 | 350 | def.Binding.Name = s.name + "Binding" 351 | def.Binding.Type = "tns:" + def.PortType.Name 352 | def.Binding.SoapBinding.Style = "rpc" 353 | def.Binding.SoapBinding.Transport = "http://schemas.xmlsoap.org/soap/http" 354 | 355 | def.Service.Name = s.name 356 | def.Service.Port = wsdl.ServicePort{ 357 | Name: s.name + "Port", 358 | Binding: "tns:" + def.Binding.Name, 359 | // Address: ServiceAddress{Location: location}, 360 | } 361 | s.wsdl = def 362 | } 363 | -------------------------------------------------------------------------------- /soap/soap.go: -------------------------------------------------------------------------------- 1 | package soap 2 | 3 | import ( 4 | "encoding/xml" 5 | ) 6 | 7 | type Envelope struct { 8 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"` 9 | XSI string `xml:"xmlns:xsi,attr"` 10 | XSD string `xml:"xmlns:xsd,attr"` 11 | Soap string `xml:"xmlns:soap,attr"` 12 | Body Body 13 | } 14 | 15 | func NewEnvelope(content []byte) *Envelope { 16 | se := &Envelope{} 17 | se.XSI = "http://www.w3.org/2001/XMLSchema-instance" 18 | se.XSD = "http://www.w3.org/2001/XMLSchema" 19 | se.Soap = "http://schemas.xmlsoap.org/soap/envelope/" 20 | se.Body.Content = content 21 | return se 22 | } 23 | 24 | type Body struct { 25 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"` 26 | Content []byte `xml:",innerxml"` 27 | } 28 | 29 | type Fault struct { 30 | XMLName xml.Name `xml:"Fault"` 31 | FaultCode string `xml:"faultcode"` 32 | FaultString string `xml:"faultstring"` 33 | Detail string `xml:"detail"` 34 | } 35 | -------------------------------------------------------------------------------- /type.go: -------------------------------------------------------------------------------- 1 | package gosoap 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "strings" 7 | 8 | "github.com/afocus/gosoap/xsd" 9 | ) 10 | 11 | func checkBaseTypeKind(k reflect.Kind) (string, error) { 12 | 13 | switch k { 14 | case reflect.String: 15 | return xsd.String, nil 16 | 17 | case reflect.Int, reflect.Int32: 18 | return xsd.Int32, nil 19 | 20 | case reflect.Int64: 21 | return xsd.Int64, nil 22 | 23 | case reflect.Bool: 24 | return xsd.Bool, nil 25 | 26 | case reflect.Float32: 27 | return xsd.Float32, nil 28 | 29 | case reflect.Float64: 30 | return xsd.Float64, nil 31 | 32 | default: 33 | return "", errors.New("xxx") 34 | } 35 | 36 | } 37 | 38 | func getTagsInfo(t reflect.StructField) (string, bool) { 39 | required := false 40 | name := t.Name 41 | tags := strings.Split(t.Tag.Get("wsdl"), ",") 42 | for k, v := range tags { 43 | tag := strings.TrimSpace(v) 44 | if k == 0 { 45 | if tag != "" { 46 | name = tag 47 | } 48 | } else { 49 | if tag == "required" { 50 | required = true 51 | break 52 | } 53 | } 54 | } 55 | return name, required 56 | } 57 | -------------------------------------------------------------------------------- /wsdl/wsdl.go: -------------------------------------------------------------------------------- 1 | package wsdl 2 | 3 | import ( 4 | "encoding/xml" 5 | 6 | "github.com/afocus/gosoap/xsd" 7 | ) 8 | 9 | type Definitions struct { 10 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/wsdl/ definitions"` 11 | SoapEnv string `xml:"xmlns:SOAP-ENV,attr"` 12 | TargetNs string `xml:"targetNamespace,attr"` 13 | Tns string `xml:"xmlns:tns,attr"` 14 | Soap string `xml:"xmlns:soap,attr"` 15 | Xsd string `xml:"xmlns:xsd,attr"` 16 | Xsi string `xml:"xmlns:xsi,attr"` 17 | Wsdl string `xml:"xmlns:wsdl,attr"` 18 | Types Type `xml:"types"` 19 | Message []Message `xml:"message"` 20 | PortType PortType `xml:"portType"` 21 | Binding Binding `xml:"binding"` 22 | Service Service `xml:"service"` 23 | } 24 | 25 | type Type struct { 26 | Schemas []xsd.Schema `xml:"schema"` 27 | } 28 | 29 | type Message struct { 30 | Name string `xml:"name,attr"` 31 | Part []Part `xml:"part"` 32 | } 33 | 34 | type Part struct { 35 | Name string `xml:"name,attr"` 36 | Type string `xml:"type,attr,omitempty"` 37 | Element string `xml:"element,attr,omitempty"` 38 | } 39 | 40 | type PortType struct { 41 | Name string `xml:"name,attr"` 42 | Operations []PortTypeOperation `xml:"operation"` 43 | } 44 | 45 | type PortTypeOperation struct { 46 | Name string `xml:"name,attr"` 47 | Input PortTypeOperationMessage `xml:"input"` 48 | Output PortTypeOperationMessage `xml:"output"` 49 | //Fault PortTypeOperationMessage `xml:"fault,omitempty"` 50 | } 51 | 52 | type PortTypeOperationMessage struct { 53 | Name string `xml:"name,attr,omitempty"` 54 | Message string `xml:"message,attr"` 55 | } 56 | 57 | type Binding struct { 58 | Name string `xml:"name,attr"` 59 | Type string `xml:"type,attr"` 60 | SoapBinding SoapBinding `xml:"soap:binding"` 61 | Operations []BindingOperation `xml:"operation"` 62 | } 63 | 64 | type SoapBinding struct { 65 | XMLName xml.Name `xml:"soap:binding"` 66 | Transport string `xml:"transport,attr"` 67 | Style string `xml:"style,attr"` 68 | } 69 | 70 | type BindingOperation struct { 71 | Name string `xml:"name,attr"` 72 | SoapOperation SoapOperation `xml:"soap:operation"` 73 | Input SoapBodyIO `xml:"input"` 74 | Output SoapBodyIO `xml:"output"` 75 | //Fault SoapBody `xml:"fault>fault,omitempty"` 76 | } 77 | 78 | type SoapOperation struct { 79 | SoapAction string `xml:"soapAction,attr"` 80 | Style string `xml:"style,attr,omitempty"` 81 | } 82 | 83 | type SoapBodyIO struct { 84 | SoapBody SoapBody `xml:"soap:body"` 85 | } 86 | 87 | type SoapBody struct { 88 | Name string `xml:"name,attr,omitempty"` 89 | Use string `xml:"use,attr"` 90 | } 91 | 92 | type Service struct { 93 | Name string `xml:"name,attr"` 94 | Port ServicePort `xml:"port"` 95 | } 96 | 97 | type ServicePort struct { 98 | XMLName xml.Name `xml:"port"` 99 | Name string `xml:"name,attr"` 100 | Binding string `xml:"binding,attr"` 101 | Address ServiceAddress `xml:"soap:address"` 102 | } 103 | 104 | type ServiceAddress struct { 105 | XMLName xml.Name `xml:"soap:address"` 106 | Location string `xml:"location,attr"` 107 | } 108 | -------------------------------------------------------------------------------- /xsd/xsd.go: -------------------------------------------------------------------------------- 1 | package xsd 2 | 3 | import ( 4 | "encoding/xml" 5 | ) 6 | 7 | const ( 8 | String string = "xsd:string" 9 | Int32 = "xsd:int" 10 | Int64 = "xsd:long" 11 | Float32 = "xsd:float" 12 | Float64 = "Xsd:double" 13 | Bool = "xsd:boolean" 14 | ) 15 | 16 | type Schema struct { 17 | XMLName xml.Name `xml:"http://www.w3.org/2001/XMLSchema xsd:schema"` 18 | TNS string `xml:"xmlns tns,attr,omitempty"` 19 | XS string `xml:"xmlns xs,attr,omitempty"` 20 | TargetNamespace string `xml:"targetNamespace,attr,omitempty"` 21 | ElementFormDefault string `xml:"elementFormDefault,attr,omitempty"` 22 | Version string `xml:"version,attr,omitempty"` 23 | Elements []Element `xml:"http://www.w3.org/2001/XMLSchema element"` 24 | ComplexTypes []ComplexType `xml:"http://www.w3.org/2001/XMLSchema complexType"` 25 | Import []Import `xml:"xsd:import"` 26 | } 27 | 28 | type Element struct { 29 | XMLName xml.Name `xml:"http://www.w3.org/2001/XMLSchema element"` 30 | Type string `xml:"type,attr,omitempty"` 31 | Nillable bool `xml:"nillable,attr"` 32 | MinOccurs int `xml:"minOccurs,attr"` 33 | MaxOccurs int `xml:"maxOccurs,attr,omitempty"` 34 | Form string `xml:"form,attr,omitempty"` 35 | Name string `xml:"name,attr"` 36 | ComplexTypes *ComplexType `xml:"http://www.w3.org/2001/XMLSchema complexType"` 37 | } 38 | 39 | type ComplexType struct { 40 | XMLName xml.Name `xml:"http://www.w3.org/2001/XMLSchema complexType"` 41 | Name string `xml:"name,attr,omitempty"` 42 | Abstract bool `xml:"abstract,attr"` 43 | Sequence []Element `xml:"sequence>element"` 44 | Content *ComplexContent `xml:"http://www.w3.org/2001/XMLSchema complexContent"` 45 | } 46 | 47 | type ComplexContent struct { 48 | XMLName xml.Name `xml:"http://www.w3.org/2001/XMLSchema complexContent"` 49 | Extension Extension `xml:"http://www.w3.org/2001/XMLSchema extension"` 50 | } 51 | 52 | type Extension struct { 53 | XMLName xml.Name `xml:"http://www.w3.org/2001/XMLSchema extension"` 54 | Base string `xml:"base,attr"` 55 | Sequence []Element `xml:"sequence>element"` 56 | } 57 | 58 | type Import struct { 59 | XMLName xml.Name `xml:"xsd:import"` 60 | SchemaLocation string `xml:"schemaLocation,attr,omitempty"` 61 | Namespace string `xml:"namespace,attr"` 62 | } 63 | --------------------------------------------------------------------------------