Golang如何通过属性读取XML数据

Golang如何通过属性读取XML数据 尝试按照此处的解释,使用Golang连接MS SharePoint,因此我编写了以下代码,它返回了XML文本:

package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

func main() {
	const myurl = "https://login.microsoftonline.com/extSTS.srf"
	const username = "myuser@mydmain.com"
	const password = "mypassword"
	const endpoint = "https://mydomain.sharepoint.com/"
	const xmlbody = `
	<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
	xmlns:a="http://www.w3.org/2005/08/addressing"
	xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
  <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
  <a:ReplyTo>
	<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
  </a:ReplyTo>
  <a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
  <o:Security s:mustUnderstand="1"
	 xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<o:UsernameToken>
	  <o:Username>` + username + `</o:Username>
	  <o:Password>` + password + `</o:Password>
	</o:UsernameToken>
  </o:Security>
</s:Header>
<s:Body>
  <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
	<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
	  <a:EndpointReference>
		<a:Address>` + endpoint + `</a:Address>
	  </a:EndpointReference>
	</wsp:AppliesTo>
	<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
	<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
	<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
  </t:RequestSecurityToken>
</s:Body>
</s:Envelope>`

	resp, err := http.Post(myurl, "text/xml", strings.NewReader(xmlbody))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))
}

上面的代码完美地完成了第一步,现在我想获取返回的字符串并读取具有 ID="Compact0" 属性的 xml 元素,所以我尝试了以下方法:

type Node struct {
	XMLName xml.Name
	Attrs   []xml.Attr `xml:"-"`
	Content []byte     `xml:",innerxml"`
	Nodes   []Node     `xml:",any"`
}

func walk(nodes []Node, f func(Node) bool) {
	for _, n := range nodes {
		if f(n) {
			walk(n.Nodes, f)
		}
	}
}

func main() {
	// My code above, followed by:
	var n Node
	var data = []byte(body)
	buf := bytes.NewBuffer(data)
	dec := xml.NewDecoder(buf)
	err = dec.Decode(&n)
	if err != nil {
		panic(err)
	}
	walk([]Node{n}, func(n Node) bool {
		if n.Attrs == "Compact0" {.  // <= ERROR
			fmt.Println(string(n.Content))
		}
		return true
	})
}

但是我遇到了这个错误:

无法将“Compact0”(无类型字符串常量)转换为 xml.AttrcompilerInvalidUntypedConversion

错误位置:

if n.Attrs == “Compact0” {}

返回的 XML 响应是:

<?xml version="1.0" encoding="utf-8"?>
<S:Envelope xmlns:wsa="http://www.w3.org/2005/08/addressing" 
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" 
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" 
xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" 
xmlns:S="http://www.w3.org/2003/05/soap-envelope">
    <S:Header>
        <wsa:Action S:mustUnderstand="1" wsu:Id="Action">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue</wsa:Action>
        <wsa:To S:mustUnderstand="1" wsu:Id="To">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
        <wsse:Security S:mustUnderstand="1">
            <wsu:Timestamp wsu:Id="TS" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <wsu:Created>2021-06-30T18:38:50.5911765Z</wsu:Created>
            <wsu:Expires>2021-06-30T18:43:50.5911765Z</wsu:Expires>
            </wsu:Timestamp>
        </wsse:Security>
    </S:Header>
    <S:Body xmlns:S="http://www.w3.org/2003/05/soap-envelope">
        <wst:RequestSecurityTokenResponse xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" x
        mlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" 
        xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
            <wst:TokenType>urn:passport:compact</wst:TokenType>
            <wsp:AppliesTo>
                <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
                    <wsa:Address>https://mydomain.sharepoint.com/</wsa:Address>
                </wsa:EndpointReference>
            </wsp:AppliesTo>
            <wst:Lifetime>
                <wsu:Created>2021-06-30T18:38:49Z</wsu:Created>
                <wsu:Expires>2021-07-01T18:38:49Z</wsu:Expires>
            </wst:Lifetime>
            <wst:RequestedSecurityToken>
                <wsse:BinarySecurityToken Id="Compact0" 
                xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                t=blablabla==&amp;p=
                </wsse:BinarySecurityToken>
            </wst:RequestedSecurityToken>
            <wst:RequestedAttachedReference>
                <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                    <wsse:Reference URI="VkETyJDRdMqocUhjsNftrfT9Z8U="></wsse:Reference>
                </wsse:SecurityTokenReference>
            </wst:RequestedAttachedReference>
            <wst:RequestedUnattachedReference>
                <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                    <wsse:Reference URI="VkETyJDRdMqocUhjsNftrfT9Z8U="></wsse:Reference>
                </wsse:SecurityTokenReference>
            </wst:RequestedUnattachedReference>
        </wst:RequestSecurityTokenResponse>
    </S:Body>
</S:Envelope>

我需要从中获取位于以下位置的 blablabla

                <wsse:BinarySecurityToken Id="Compact0" 
                xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                t=blablabla==&amp;p=
                </wsse:BinarySecurityToken>

更多关于Golang如何通过属性读取XML数据的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

问题之一可能是你正在尝试将一个字符串与一个 xml.Attr 数组进行比较。

更多关于Golang如何通过属性读取XML数据的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中通过属性读取XML数据,需要使用xml.Attr类型来访问属性。你的代码中n.Attrs是一个[]xml.Attr切片,不能直接与字符串比较。以下是修正后的代码:

package main

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type Node struct {
	XMLName xml.Name
	Attrs   []xml.Attr `xml:",any,attr"`
	Content []byte     `xml:",innerxml"`
	Nodes   []Node     `xml:",any"`
}

func walk(nodes []Node, f func(Node) bool) {
	for _, n := range nodes {
		if f(n) {
			walk(n.Nodes, f)
		}
	}
}

func main() {
	const myurl = "https://login.microsoftonline.com/extSTS.srf"
	const username = "myuser@mydmain.com"
	const password = "mypassword"
	const endpoint = "https://mydomain.sharepoint.com/"
	const xmlbody = `
	<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
	xmlns:a="http://www.w3.org/2005/08/addressing"
	xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
  <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
  <a:ReplyTo>
	<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
  </a:ReplyTo>
  <a:To s:mustUnderstand="1">https://login.microsoftonline.com/extSTS.srf</a:To>
  <o:Security s:mustUnderstand="1"
	 xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
	<o:UsernameToken>
	  <o:Username>` + username + `</o:Username>
	  <o:Password>` + password + `</o:Password>
	</o:UsernameToken>
  </o:Security>
</s:Header>
<s:Body>
  <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
	<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
	  <a:EndpointReference>
		<a:Address>` + endpoint + `</a:Address>
	  </a:EndpointReference>
	</wsp:AppliesTo>
	<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
	<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
	<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
  </t:RequestSecurityToken>
</s:Body>
</s:Envelope>`

	resp, err := http.Post(myurl, "text/xml", strings.NewReader(xmlbody))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Println("response Body:", string(body))

	var n Node
	var data = []byte(body)
	buf := bytes.NewBuffer(data)
	dec := xml.NewDecoder(buf)
	err = dec.Decode(&n)
	if err != nil {
		panic(err)
	}

	walk([]Node{n}, func(n Node) bool {
		for _, attr := range n.Attrs {
			if attr.Name.Local == "Id" && attr.Value == "Compact0" {
				fmt.Println("Found BinarySecurityToken with Id='Compact0':")
				fmt.Println(string(n.Content))
				return false
			}
		}
		return true
	})
}

关键修改:

  1. Attrs字段的标签改为xml:",any,attr"以正确捕获所有属性
  2. walk函数中遍历n.Attrs切片,检查每个属性的Name.LocalValue
  3. 当找到Id="Compact0"的属性时,打印节点的内容

另一种更直接的方法是使用结构体映射:

type BinarySecurityToken struct {
	XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd BinarySecurityToken"`
	Id      string   `xml:"Id,attr"`
	Content string   `xml:",chardata"`
}

type RequestedSecurityToken struct {
	BinarySecurityToken BinarySecurityToken `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd BinarySecurityToken"`
}

type RequestSecurityTokenResponse struct {
	XMLName                  xml.Name                 `xml:"http://schemas.xmlsoap.org/ws/2005/02/trust RequestSecurityTokenResponse"`
	RequestedSecurityToken   RequestedSecurityToken   `xml:"RequestedSecurityToken"`
}

type Body struct {
	RequestSecurityTokenResponse RequestSecurityTokenResponse `xml:"http://schemas.xmlsoap.org/ws/2005/02/trust RequestSecurityTokenResponse"`
}

type Envelope struct {
	XMLName xml.Name `xml:"http://www.w3.org/2003/05/soap-envelope Envelope"`
	Body    Body     `xml:"Body"`
}

func main() {
	// ... 前面的HTTP请求代码保持不变 ...
	
	var envelope Envelope
	err = xml.Unmarshal(body, &envelope)
	if err != nil {
		panic(err)
	}

	token := envelope.Body.RequestSecurityTokenResponse.RequestedSecurityToken.BinarySecurityToken
	if token.Id == "Compact0" {
		fmt.Println("BinarySecurityToken content:", token.Content)
	}
}

这种方法通过定义精确的XML结构体映射,可以直接访问Id="Compact0"BinarySecurityToken元素内容。

回到顶部