使用Go解析SOAP消息

huangapple go评论88阅读模式
英文:

Unmarshal SOAP message with Go

问题

我相对于Go语言还比较新手。

我在尝试解析一个SOAP消息时遇到了问题。我的目标是抽象出Body元素的内容,并避免静态定义XML结构,因为它根据请求的操作而变化。
不幸的是,我找不到正确的方法来实现这一点。在这个例子中,GetContent函数应该接收一个包含内容的结构体的指针,并动态地将其添加到Body中以进行填充。但结果并不是预期的。

以下是示例代码:

package main

import (
	"encoding/xml"
	"fmt"
)

type Message interface{}

type EnvelopeResponse struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Body    Message  `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type Body struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`

	Fault               *Fault  `xml:",omitempty"`
	Content             Message `xml:",omitempty"`
	SOAPBodyContentType string  `xml:"-"`
}

type Fault struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`

	Code   string `xml:"faultcode,omitempty"`
	String string `xml:"faultstring,omitempty"`
	Actor  string `xml:"faultactor,omitempty"`
	Detail string `xml:"detail,omitempty"`
}

type GetHostNumberOfEntriesResponse struct {
	XMLName                xml.Name `xml:"urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse"`
	NewHostNumberOfEntries int64    `xml:"NewHostNumberOfEntries"`
}

func GetContent(rawXml []byte, content interface{}) {
	envelope := EnvelopeResponse{Body: Body{Content: content}}
	xml.Unmarshal(rawXml, &envelope)
}

func main() {
	b := []byte(`
	<?xml version="1.0"?>
	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<s:Body>
	<u:GetHostNumberOfEntriesResponse xmlns:u="urn:dslforum-org:service:Hosts:1">
	<NewHostNumberOfEntries>47</NewHostNumberOfEntries>
	</u:GetHostNumberOfEntriesResponse>
	</s:Body>
	</s:Envelope>
	`)
	content := &GetHostNumberOfEntriesResponse{}
	GetContent(b, content)
	fmt.Println(*content)
}

你可以在这里的playground中查看示例代码:https://go.dev/play/p/BBR4vEXiPbc

英文:

I'm relatively new to the go language.

I have a problem trying to unmarshal a SOAP message. My attempt is to abstract the content of the Body element and avoid defining the XML structure statically, as it changes depending on the requested action.
Unfortunately I can't find a way to do this correctly. In the example, the GetContent function should receive a pointer to the struct that contains the content and add it dynamically to the Body, in order to be filled. But the result is not the expected one.

package main
import (
&quot;encoding/xml&quot;
&quot;fmt&quot;
)
type Message interface{}
type EnvelopeResponse struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Envelope&quot;`
Body    Message  `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Body&quot;`
}
type Body struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Body&quot;`
Fault               *Fault  `xml:&quot;,omitempty&quot;`
Content             Message `xml:&quot;,omitempty&quot;`
SOAPBodyContentType string  `xml:&quot;-&quot;`
}
type Fault struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Fault&quot;`
Code   string `xml:&quot;faultcode,omitempty&quot;`
String string `xml:&quot;faultstring,omitempty&quot;`
Actor  string `xml:&quot;faultactor,omitempty&quot;`
Detail string `xml:&quot;detail,omitempty&quot;`
}
type GetHostNumberOfEntriesResponse struct {
XMLName                xml.Name `xml:&quot;urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse&quot;`
NewHostNumberOfEntries int64    `xml:&quot;NewHostNumberOfEntries&quot;`
}
func GetContent(rawXml []byte, content interface{}) {
envelope := EnvelopeResponse{Body: Body{Content: content}}
xml.Unmarshal(rawXml, &amp;envelope)
}
func main() {
b := []byte(`
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;s:Envelope xmlns:s=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot; s:encodingStyle=&quot;http://schemas.xmlsoap.org/soap/encoding/&quot;&gt;
&lt;s:Body&gt;
&lt;u:GetHostNumberOfEntriesResponse xmlns:u=&quot;urn:dslforum-org:service:Hosts:1&quot;&gt;
&lt;NewHostNumberOfEntries&gt;47&lt;/NewHostNumberOfEntries&gt;
&lt;/u:GetHostNumberOfEntriesResponse&gt;
&lt;/s:Body&gt;
&lt;/s:Envelope&gt;
`)
content := &amp;GetHostNumberOfEntriesResponse{}
GetContent(b, content)
fmt.Println(*content)
}

Here the example in the playground:

https://go.dev/play/p/BBR4vEXiPbc

答案1

得分: 0

你使用了一些interface{},为什么?以下是测试通过的代码:

package main

import (
	"encoding/xml"
	"fmt"
)

type Message interface{}

type EnvelopeResponse struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Body    Body
}

type Body struct {
	XMLName                        xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
	GetHostNumberOfEntriesResponse GetHostNumberOfEntriesResponse
	Fault                          *Fault  `xml:",omitempty"`
	Content                        Message `xml:",omitempty"`
	SOAPBodyContentType            string  `xml:"-"`
}

type Fault struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
	Code    string   `xml:"faultcode,omitempty"`
	String  string   `xml:"faultstring,omitempty"`
	Actor   string   `xml:"faultactor,omitempty"`
	Detail  string   `xml:"detail,omitempty"`
}

type GetHostNumberOfEntriesResponse struct {
	XMLName                xml.Name `xml:"urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse"`
	NewHostNumberOfEntries int64    `xml:"NewHostNumberOfEntries"`
}

func GetContent(rawXml []byte) *EnvelopeResponse {
	envelope := EnvelopeResponse{}
	xml.Unmarshal(rawXml, &envelope)
	return &envelope
}

func main() {
	b := []byte(`
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<s:Body>
		<u:GetHostNumberOfEntriesResponse xmlns:u="urn:dslforum-org:service:Hosts:1">
			<NewHostNumberOfEntries>47</NewHostNumberOfEntries>
		</u:GetHostNumberOfEntriesResponse>
	</s:Body>
</s:Envelope>
`)
	envelope := GetContent(b)
	fmt.Println(envelope)
}
&{{http://schemas.xmlsoap.org/soap/envelope/ Envelope} {{http://schemas.xmlsoap.org/soap/envelope/ Body} {{urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse} 47} <nil> <nil> 
}}
英文:

you use some interface{}, Why? Follow is test pass code:
https://go.dev/play/p/OPkNScHjri1

package main
import (
&quot;encoding/xml&quot;
&quot;fmt&quot;
)
type Message interface{}
type EnvelopeResponse struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Envelope&quot;`
Body    Body
}
type Body struct {
XMLName                        xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Body&quot;`
GetHostNumberOfEntriesResponse GetHostNumberOfEntriesResponse
Fault                          *Fault  `xml:&quot;,omitempty&quot;`
Content                        Message `xml:&quot;,omitempty&quot;`
SOAPBodyContentType            string  `xml:&quot;-&quot;`
}
type Fault struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Fault&quot;`
Code    string   `xml:&quot;faultcode,omitempty&quot;`
String  string   `xml:&quot;faultstring,omitempty&quot;`
Actor   string   `xml:&quot;faultactor,omitempty&quot;`
Detail  string   `xml:&quot;detail,omitempty&quot;`
}
type GetHostNumberOfEntriesResponse struct {
XMLName                xml.Name `xml:&quot;urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse&quot;`
NewHostNumberOfEntries int64    `xml:&quot;NewHostNumberOfEntries&quot;`
}
func GetContent(rawXml []byte) *EnvelopeResponse {
envelope := EnvelopeResponse{}
xml.Unmarshal(rawXml, &amp;envelope)
return &amp;envelope
}
func main() {
b := []byte(`
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;s:Envelope xmlns:s=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot; s:encodingStyle=&quot;http://schemas.xmlsoap.org/soap/encoding/&quot;&gt;
&lt;s:Body&gt;
&lt;u:GetHostNumberOfEntriesResponse xmlns:u=&quot;urn:dslforum-org:service:Hosts:1&quot;&gt;
&lt;NewHostNumberOfEntries&gt;47&lt;/NewHostNumberOfEntries&gt;
&lt;/u:GetHostNumberOfEntriesResponse&gt;
&lt;/s:Body&gt;
&lt;/s:Envelope&gt;
`)
envelope := GetContent(b)
fmt.Println(envelope)
}
&amp;{{http://schemas.xmlsoap.org/soap/envelope/ Envelope} {{http://schemas.xmlsoap.org/soap/envel
ope/ Body} {{urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse} 47} &lt;nil&gt; &lt;nil&gt; 
}}

答案2

得分: 0

我找到的解决方案是使用泛型来表示主体的变量和参数化内容。

以下是我期望的代码:

package main

import (
	"encoding/xml"
	"fmt"
)

type EnvelopeResponse[T any] struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Body    Body[T]  `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type Body[T any] struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`

	Fault               *Fault `xml:",omitempty"`
	Content             T      `xml:",omitempty"`
	SOAPBodyContentType string `xml:"-"`
}

type Fault struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`

	Code   string `xml:"faultcode,omitempty"`
	String string `xml:"faultstring,omitempty"`
	Actor  string `xml:"faultactor,omitempty"`
	Detail string `xml:"detail,omitempty"`
}

type GetHostNumberOfEntriesResponse struct {
	XMLName                xml.Name `xml:"urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse"`
	NewHostNumberOfEntries int64    `xml:"NewHostNumberOfEntries"`
}

func GetContent[T any](rawXml []byte, content T) {
	envelope := EnvelopeResponse[T]{Body: Body[T]{Content: content}}
	xml.Unmarshal(rawXml, &envelope)
}

func main() {
	b := []byte(`
	<?xml version="1.0"?>
	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<s:Body>
	<u:GetHostNumberOfEntriesResponse xmlns:u="urn:dslforum-org:service:Hosts:1">
	<NewHostNumberOfEntries>47</NewHostNumberOfEntries>
	</u:GetHostNumberOfEntriesResponse>
	</s:Body>
	</s:Envelope>
	`)
	content := &GetHostNumberOfEntriesResponse{}
	GetContent(b, content)
	fmt.Println(*content)
}

希望对你有帮助!

英文:

The solution I found is to use generics to represent the variable and parameterized content of the body.

This code works as I expect:

package main
import (
&quot;encoding/xml&quot;
&quot;fmt&quot;
)
type EnvelopeResponse[T any] struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Envelope&quot;`
Body    Body[T]  `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Body&quot;`
}
type Body[T any] struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Body&quot;`
Fault               *Fault `xml:&quot;,omitempty&quot;`
Content             T      `xml:&quot;,omitempty&quot;`
SOAPBodyContentType string `xml:&quot;-&quot;`
}
type Fault struct {
XMLName xml.Name `xml:&quot;http://schemas.xmlsoap.org/soap/envelope/ Fault&quot;`
Code   string `xml:&quot;faultcode,omitempty&quot;`
String string `xml:&quot;faultstring,omitempty&quot;`
Actor  string `xml:&quot;faultactor,omitempty&quot;`
Detail string `xml:&quot;detail,omitempty&quot;`
}
type GetHostNumberOfEntriesResponse struct {
XMLName                xml.Name `xml:&quot;urn:dslforum-org:service:Hosts:1 GetHostNumberOfEntriesResponse&quot;`
NewHostNumberOfEntries int64    `xml:&quot;NewHostNumberOfEntries&quot;`
}
func GetContent[T any](rawXml []byte, content T) {
envelope := EnvelopeResponse[T]{Body: Body[T]{Content: content}}
xml.Unmarshal(rawXml, &amp;envelope)
}
func main() {
b := []byte(`
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;s:Envelope xmlns:s=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot; s:encodingStyle=&quot;http://schemas.xmlsoap.org/soap/encoding/&quot;&gt;
&lt;s:Body&gt;
&lt;u:GetHostNumberOfEntriesResponse xmlns:u=&quot;urn:dslforum-org:service:Hosts:1&quot;&gt;
&lt;NewHostNumberOfEntries&gt;47&lt;/NewHostNumberOfEntries&gt;
&lt;/u:GetHostNumberOfEntriesResponse&gt;
&lt;/s:Body&gt;
&lt;/s:Envelope&gt;
`)
content := &amp;GetHostNumberOfEntriesResponse{}
GetContent(b, content)
fmt.Println(*content)
}

huangapple
  • 本文由 发表于 2023年2月7日 04:54:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/75366482.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定