package base import ( "bytes" "crypto/md5" "encoding/hex" "encoding/xml" "errors" "hash" "io" "sort" "sync" ) var textBufferPool = sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, 16<<10)) // 16KB }, } // FormatMapToXML marshal map[string]string to xmlWriter with xml format, the root node name is xml. // NOTE: This function assumes the key of m map[string]string are legitimate xml name string // that does not contain the required escape character! func FormatMapToXML(xmlWriter io.Writer, m map[string]string) (err error) { if xmlWriter == nil { return errors.New("nil xmlWriter") } if _, err = io.WriteString(xmlWriter, ""); err != nil { return } for k, v := range m { if _, err = io.WriteString(xmlWriter, "<"+k+">"); err != nil { return } if err = xml.EscapeText(xmlWriter, []byte(v)); err != nil { return } if _, err = io.WriteString(xmlWriter, ""); err != nil { return } } if _, err = io.WriteString(xmlWriter, ""); err != nil { return } return } //Sign 微信支付签名. // parameters: 待签名的参数集合 // apiKey: API密钥 // fn: func() hash.Hash, 如果 fn == nil 则默认用 md5.New func Sign(parameters map[string]string, apiKey string, fn func() hash.Hash) string { ks := make([]string, 0, len(parameters)) for k := range parameters { if k == "sign" { continue } ks = append(ks, k) } sort.Strings(ks) if fn == nil { fn = md5.New } h := fn() buf := make([]byte, 256) for _, k := range ks { v := parameters[k] if v == "" { continue } buf = buf[:0] buf = append(buf, k...) buf = append(buf, '=') buf = append(buf, v...) buf = append(buf, '&') h.Write(buf) } buf = buf[:0] buf = append(buf, "key="...) buf = append(buf, apiKey...) h.Write(buf) signature := make([]byte, h.Size()*2) hex.Encode(signature, h.Sum(nil)) return string(bytes.ToUpper(signature)) } // ParseXMLToMap parses xml reading from xmlReader and returns the first-level sub-node key-value set, // if the first-level sub-node contains child nodes, skip it. func ParseXMLToMap(xmlReader io.Reader) (m map[string]string, err error) { if xmlReader == nil { err = errors.New("nil xmlReader") return } m = make(map[string]string) var ( d = xml.NewDecoder(xmlReader) tk xml.Token depth = 0 // current xml.Token depth key string value bytes.Buffer ) for { tk, err = d.Token() if err != nil { if err == io.EOF { err = nil } return } switch v := tk.(type) { case xml.StartElement: depth++ switch depth { case 2: key = v.Name.Local value.Reset() case 3: if err = d.Skip(); err != nil { return } depth-- key = "" // key == "" indicates that the node with depth==2 has children } case xml.CharData: if depth == 2 && key != "" { value.Write(v) } case xml.EndElement: if depth == 2 && key != "" { m[key] = value.String() } depth-- } } }