Are there any packages to support SOAP/WSDL on Go?
-
Thanks for small XML/SOAP package, did the job well. Guys can find the lib here -- https://github.com/webconnex/xmlutil – Knights Sep 10 '15 at 13:10
-
1I often have to use SOAP on legacy systems, and been doing it by hard coding structures in Go until last weekend. Hacked up a parser + Go code generator for WSDL that can produce usable Go code to call SOAP. Some of the APIs I use are quite extensive, generating files with over 2k LOC. Check it out: https://github.com/fiorix/wsdl2go – fiorix Mar 22 '16 at 03:34
4 Answers
There isn't support for WSDL in Go. Support in other languages are either static or dynamic: Either structs are pre-generated from the WSDL, or it's done on the fly with hash tables.
You can, however, encode and decode SOAP requests manually. I found that the standard encoding/xml
package to be insufficient for SOAP. There are so many quirks in different servers, and the limitations in encoding/xml
make it difficult generate a request these servers are happy with.
For example, some servers need xsi:type="xsd:string"
on every string tag. In order to do this properly your struct needs to look like this for encoding/xml
:
type MethodCall struct {
One XSI
Two XSI
}
type XSI struct {
Type string `xml:"xsi:type,attr"`
Vaue string `xml:",chardata"`
}
And you construct it like this:
MethodCall{
XSI{"xsd:string", "One"},
XSI{"xsd:string", "Two"},
}
Which gives you:
<MethodCall>
<One xsi:type="xsd:string">One</One>
<Two xsi:type="xsd:string">Two</Two>
</MethodCall>
Now this might be ok. It certainly gets the job done. But what if you needed more than just a string
? encoding/xml
currently doesn't support interface{}
.
As you can see this gets complicated. If you had one SOAP API to integrate, this probably wouldn't be too bad. What if you had several, each with their own quirks?
Wouldn't it be nice if you could just do this?
type MethodCall struct {
One string
Two string
}
Then say to encoding/xml
: "This server want xsi types".
To solve this problem I created github.com/webconnex/xmlutil. It's a work in progress. It doesn't have all the features of encoding/xml
's encoder/decoder, but it has what is needed for SOAP.
Here's a working example:
package main
import (
"bytes"
"encoding/xml"
"fmt"
"github.com/webconnex/xmlutil"
"log"
//"net/http"
)
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
type MethodCall struct {
One string
Two string
}
type MethodCallResponse struct {
Three string
}
func main() {
x := xmlutil.NewXmlUtil()
x.RegisterNamespace("http://www.w3.org/2001/XMLSchema-instance", "xsi")
x.RegisterNamespace("http://www.w3.org/2001/XMLSchema", "xsd")
x.RegisterNamespace("http://www.w3.org/2003/05/soap-envelope", "soap")
x.RegisterTypeMore(Envelope{}, xml.Name{"http://www.w3.org/2003/05/soap-envelope", ""},
[]xml.Attr{
xml.Attr{xml.Name{"xmlns", "xsi"}, "http://www.w3.org/2001/XMLSchema-instance"},
xml.Attr{xml.Name{"xmlns", "xsd"}, "http://www.w3.org/2001/XMLSchema"},
xml.Attr{xml.Name{"xmlns", "soap"}, "http://www.w3.org/2003/05/soap-envelope"},
})
x.RegisterTypeMore("", xml.Name{}, []xml.Attr{
xml.Attr{xml.Name{"http://www.w3.org/2001/XMLSchema-instance", "type"}, "xsd:string"},
})
buf := new(bytes.Buffer)
buf.WriteString(`<?xml version="1.0" encoding="utf-8"?>`)
buf.WriteByte('\n')
enc := x.NewEncoder(buf)
env := &Envelope{Body{MethodCall{
One: "one",
Two: "two",
}}}
if err := enc.Encode(env); err != nil {
log.Fatal(err)
}
// Print request
bs := buf.Bytes()
bs = bytes.Replace(bs, []byte{'>', '<'}, []byte{'>', '\n', '<'}, -1)
fmt.Printf("%s\n\n", bs)
/*
// Send response, SOAP 1.2, fill in url, namespace, and action
var r *http.Response
if r, err = http.Post(url, "application/soap+xml; charset=utf-8; action="+namespace+"/"+action, buf); err != nil {
return
}
dec := x.NewDecoder(r.Body)
*/
// Decode response
dec := x.NewDecoder(bytes.NewBufferString(`<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<soap:Body>
<MethodCallResponse>
<Three>three</Three>
</MethodCallResponse>
</soap:Body>
</soap:Envelope>`))
find := []xml.Name{
xml.Name{"", "MethodCallResponse"},
xml.Name{"http://www.w3.org/2003/05/soap-envelope", "Fault"},
}
var start *xml.StartElement
var err error
if start, err = dec.Find(find); err != nil {
log.Fatal(err)
}
if start.Name.Local == "Fault" {
log.Fatal("Fault!") // Here you can decode a Soap Fault
}
var resp MethodCallResponse
if err := dec.DecodeElement(&resp, start); err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n\n", resp)
}
With the above example I use the Find
method to get the response object, or a Fault. This isn't strictly necessary. You can also do it like this:
x.RegisterType(MethodCallResponse{})
...
// Decode response
dec := x.NewDecoder(bytes.NewBufferString(`<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<soap:Body>
<MethodCallResponse>
<Three>three</Three>
</MethodCallResponse>
</soap:Body>
</soap:Envelope>`))
var start *xml.StartElement
var resp Envelope
if err := dec.DecodeElement(&resp, start); err != nil {
log.Fatal(err)
}
fmt.Printf("%#v\n\n", resp)
You'll find the Find
method useful when your data looks like this:
<soap:Envelope>
<soap:Body>
<MethodResponse>
<MethodResult>
<diffgr:diffgram>
<NewDataSet>
<Table1 diffgr:id="Table1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<Three>three</Three>
</Table1>
</NewDataSet>
</diffgr:diffgram>
</MethodResult>
</MethodResponse>
</soap:Body>
</soap:Envelope>
This is a DiffGram, part of Microsoft .NET. You can use the Find
method to get to Table1
. The Decode
and DecodeElement
method also works on slices. So you can pass in a []MethodCallResponse
if NewDataSet
happens to contain more than one result.
I do agree with Zippower that SOAP does suck. But unfortunately a lot of enterprises use SOAP, and you're sometimes forced to use these APIs. With the xmlutil package I hope to make it a little less painful to work with.

- 13,678
- 7
- 45
- 79
-
7Go tip now supports [Marsalers](http://tip.golang.org/pkg/encoding/xml/#Marshaler) and [Unmarshalers](http://tip.golang.org/pkg/encoding/xml/#Unmarshaler) in encoding/xml like encoding/json already did, and this feature is expected to be in Go 1.2. This might help with handling SOAP. – Matt Sep 04 '13 at 14:29
-
Works great @luke, your small xml utility lib...js needs a get started documentation, though with a few minutes of scanning thru the script above I was able to understand how to go by things – Knights Sep 10 '15 at 13:08
-
1I wrote a WSDL parser that can generate Go code to call SOAP. There's a lot of limitations in encoding/xml with regard to tags, and I put a link to that in the README. Check it out: https://github.com/fiorix/wsdl2go – fiorix Mar 22 '16 at 03:37
Nope.
SOAP sucks, but I had to implement a server of an already-defined protocol that uses SOAP, so I listened with net/http
and decoded/encoded envelopes with encoding/xml
. In few minutes, I already served my first envelope with Go.

- 15,850
- 10
- 60
- 58
-
4Sure does, but there's an infinity of enterprise systems that only support SOAP. For those cases, we still need something useful. – fiorix Mar 22 '16 at 03:35
While there's still nothing in Go itself, there is gowsdl. So far, it seems to work well enough for me to interface with several SOAP services.
I don't use the SOAP proxy it provides, which I believe doesn't support auth, but gowsdl generates the structs and code I need from the WSDL to marshal requests and unmarshal responses--a big win.
one option is to use gsoap which produces a C WSDL client and then use that client through GO with cgo

- 4,485
- 1
- 27
- 31
-
2I'd reconsider "Best Option"! It's a complex and confusing approach – Arman Safikhani Jan 16 '18 at 14:10
-
gsoap is probably the most stable and complete soap implementation available: if you can work out the hassle of using C / C++ from Go ...then its the way to Go :) no pun intended – Edoardo Jan 16 '18 at 16:46
-
1Having dealt with gSoap from C++ I would strongly recommend against this. The gSoap code is some of the worst code I have ever seen (and that includes code from hardware vendors!). It is beyond awful. Do not use it. Whenever you're faced with a choice where one of the options is gSoap, choose the other option. Even if that option is 'write your own'. – Kristof Provost Jun 25 '18 at 08:33
-
@eddyce I am quite serious. I'd tell you to go look at the code, but you seem like a nice person, so don't do that to yourself. One example of the problems with gsoap is that it does not layer its code at all. In a soap library I'd expect to see a layer for http, one for xml and one for soap (or external libraries to handle this). gsoap intermingles all of that. There is no layering. – Kristof Provost Jun 25 '18 at 10:33
-
@KristofProvost I never do (look at the code I mean). a wsdl client is supposed to create code that works, it doesn't have to be nice or readable cause I will never even open it. I dropped the "Best Option" on my answer, cause you are not alone thinking like that, on the other hand I have seen gsoap come out with a good interface on pretty complex wsdl's and I'd use it again if I were to. – Edoardo Jun 25 '18 at 13:03