代码之家  ›  专栏  ›  技术社区  ›  Sam

在go中解组特定SOAP响应

  •  1
  • Sam  · 技术社区  · 7 年前

    我正在尝试使用下面的结构来解组以下SOAP响应。

    var data = `<?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3rg/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <soap:Body>
            <doSendResponse>
                <doSendResult>Send OK.&lt;ReturnIDs&gt;c71cf425f5;e5e4dbb5ca&lt;/ReturnIDs&gt;</doSendResult>
            </doSendResponse>
        </soap:Body>
    </soap:Envelope>`
    
    type ResponseBody struct {
        ResponseBody SendResponse `xml:"Body"`
    }
    type SendResponse struct {
        Result Result `xml:"doSendResponse"`
    }
    type Result struct {
        RawMessage string `xml:"doSendResult"`
    }
    

    一切顺利,直到 <doSendResult> 要素
    此特定标记包含一条消息,即“发送确定”和HTML编码 <ReturnIDs> 元素,问题不在于HTML编码的部分,我已经看到了 this question and the accepted answer. 我的问题是,我无法同时提取消息和返回ID。

    我尝试使用前面提到的问题中建议的方法,但失败了, Here 这是我迄今为止所尝试的。

    package main
    
    import (
        "encoding/xml"
        "fmt"
    )
    
    var data = `<?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3rg/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <soap:Body>
            <doSendResponse>
                <doSendResult>Send OK.&lt;ReturnIDs&gt;c71cf425f5;e5e4dbb5ca&lt;/ReturnIDs&gt;</doSendResult>
            </doSendResponse>
        </soap:Body>
    </soap:Envelope>`
    
    type ResponseBody struct {
        ResponseBody SendResponse `xml:"Body"`
    }
    type SendResponse struct {
        Result Result `xml:"doSendResponse"`
    }
    type Result struct {
        RawMessage string `xml:"doSendResult"`
    }
    type RawMessage struct {
        IDs     string `xml:"ReturnIDs"`
    }
    
    func main() {
        var response ResponseBody
        err := xml.Unmarshal([]byte(data), &response)
        if err != nil {
            panic(err.Error())
        }
        fmt.Printf("%+v\n", response)
    
        var rawMessage RawMessage
        err = xml.Unmarshal([]byte(response.ResponseBody.Result.RawMessage), &rawMessage)
        if err != nil {
            panic(err.Error())
        }
        fmt.Printf("%+v\n", rawMessage)
    
    }
    

    输出:
    {ResponseBody:{Result:{RawMessage:Send OK.<ReturnIDs>c71cf425f5;e5e4dbb5ca</ReturnIDs>}}} {IDs:} 我还尝试取消对响应的屏蔽,然后尝试取消对其的屏蔽,这部分是有效的,但这种方法有三个主要问题:

    1. 太慢了
    2. 我只能 get the ReturnIDs the message ,而不是两者兼而有之。
    3. 我相信这只是一个丑陋的黑客,必须有更好的方法来做到这一点(我还不知道)

    那么,如何提取消息的两个值(Send OK)以及 <ReturnID> ?

    2 回复  |  直到 7 年前
        1
  •  0
  •   Jessé Catrinck    7 年前

    您可以通过多种方式解码doSendResult标记内容,但我做了一个示例:

    play.golang.org/p/NC9YrWqK0k

    我定义了与soap信封主体内的标签相对应的两种类型:

    type (
        SendResponse struct {
            SendResult SendResult `xml:"Body>doSendResponse>doSendResult"`
        }
    
        SendResult struct {
            RawMessage string   `xml:"-"`
            Text       string   `xml:"-"`
            IDS        []string `xml:"-"`
        }
    )
    

    类型 SendResult 具有自定义解组功能,用于读取原始消息并填充结构

    func (sr *SendResult) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
        var raw string
        d.DecodeElement(&raw, &start)
    
        var st struct {
            Contents  string `xml:",chardata"`
            ReturnIDs string `xml:"ReturnIDs"`
        }
    
        err := xml.Unmarshal([]byte("<xml>"+raw+"</xml>"), &st)
        if err != nil {
            panic(err.Error())
        }
    
        sr.RawMessage = raw
        sr.Text = st.Contents
        sr.IDS = strings.Split(st.ReturnIDs, ";")
    
        return nil
    }
    

    以下是使用方法:

    const data = `<?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3rg/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <soap:Body>
            <doSendResponse>
                <doSendResult>Send OK.&lt;ReturnIDs&gt;c71cf425f5;e5e4dbb5ca&lt;/ReturnIDs&gt;</doSendResult>
            </doSendResponse>
        </soap:Body>
    </soap:Envelope>`
    
    func main() {
        var sendResponse SendResponse
    
        err := xml.Unmarshal([]byte(data), &sendResponse)
        if err != nil {
            panic(err.Error())
        }
    
        fmt.Printf("%+v\n", sendResponse)
    }
    
        2
  •  0
  •   maerics    7 年前

    doSendResult 元素似乎是一种“自定义”格式(与格式良好的文档(如HTML、XML等)相反),正则表达式可能是解析结果的好方法。

    例如:

    type SendResult struct {
      Status    string
      ReturnIds []string
    }
    
    var doSendResultRegex = regexp.MustCompile("^Send (.*?)\\.<ReturnIDs>(.*?)</ReturnIDs>$")
    
    func ParseSendResult(s string) *SendResult {
      ss := doSendResultRegex.FindStringSubmatch(s)
      if ss == nil {
        return nil
      }
      return &SendResult{
        Status:    ss[1],
        ReturnIds: strings.Split(ss[2], ";"),
      }
    }
    
    // ...
    fmt.Println("%#v\n", ParseSendResult(response.Result.RawMessage))
    // &main.SendResult{
    //   Status:    "OK",
    //   ReturnIds: []string{"c71cf425f5", "e5e4dbb5ca"}
    // }
    

    当然,您可能需要修改 doSendResultRegex 表达式取决于该数据的其他示例,但上面的代码应该说明这个想法。