github.com/polarismesh/polaris@v1.17.8/apiserver/eurekaserver/xml.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 package eurekaserver 19 20 import ( 21 "encoding/xml" 22 "errors" 23 "io" 24 "strconv" 25 "strings" 26 ) 27 28 const ( 29 attributeNotion = `@` 30 // 存量的属性前缀 31 attributeNotionCross = `-` 32 ) 33 34 // ===================================== where the work happens ============================= 35 36 // xmlToMapParser (2015.11.12) - load a 'clean' XML doc into a map[string]interface{} directly. 37 // A refactoring of xmlToTreeParser(), markDuplicate() and treeToMap() - here, all-in-one. 38 // We've removed the intermediate *node tree with the allocation and subsequent rescanning. 39 func xmlToMapParser(skey string, a []xml.Attr, p *xml.Decoder, r bool) (map[string]interface{}, error) { 40 // NOTE: all attributes and sub-elements parsed into 'na', 'na' is returned as value for 'skey' 41 // Unless 'skey' is a simple element w/o attributes, in which case the xml.CharData value is the value. 42 var n, na map[string]interface{} 43 44 // Allocate maps and load attributes, if any. 45 if skey != "" { 46 n = make(map[string]interface{}) // old n 47 na = make(map[string]interface{}) // old n.nodes 48 if len(a) > 0 { 49 for _, v := range a { 50 na[attributeNotion+v.Name.Local] = cast(v.Value, r) 51 } 52 } 53 } 54 for { 55 t, err := p.Token() 56 if err != nil { 57 if err != io.EOF { 58 return nil, errors.New("xml.Decoder.Token() - " + err.Error()) 59 } 60 return nil, err 61 } 62 switch value := t.(type) { 63 case xml.StartElement: 64 tt := value 65 66 // First call to xmlToMapParser() doesn't pass xml.StartElement - the map key. 67 // So when the loop is first entered, the first token is the root tag along 68 // with any attributes, which we process here. 69 // 70 // Subsequent calls to xmlToMapParser() will pass in tag+attributes for 71 // processing before getting the next token which is the element value, 72 // which is done above. 73 if skey == "" { 74 return xmlToMapParser(tt.Name.Local, tt.Attr, p, r) 75 } 76 77 // If not initializing the map, parse the element. 78 // len(nn) == 1, necessarily - it is just an 'n'. 79 nn, err := xmlToMapParser(tt.Name.Local, tt.Attr, p, r) 80 if err != nil { 81 return nil, err 82 } 83 84 // The nn map[string]interface{} value is a na[nn_key] value. 85 // We need to see if nn_key already exists - means we're parsing a list. 86 // This may require converting na[nn_key] value into []interface{} type. 87 // First, extract the key:val for the map - it's a singleton. 88 var key string 89 var val interface{} 90 for key, val = range nn { 91 break 92 } 93 94 // 'na' holding sub-elements of n. 95 // See if 'key' already exists. 96 // If 'key' exists, then this is a list, if not just add key:val to na. 97 if v, ok := na[key]; ok { 98 var a []interface{} 99 switch key := v.(type) { 100 case []interface{}: 101 a = key 102 default: // anything else - note: v.(type) != nil 103 a = []interface{}{v} 104 } 105 a = append(a, val) 106 na[key] = a 107 } else { 108 na[key] = val // save it as a singleton 109 } 110 case xml.EndElement: 111 // len(n) > 0 if this is a simple element w/o xml.Attrs - see xml.CharData case. 112 if len(n) == 0 { 113 // If len(na)==0 we have an empty element == ""; 114 // it has no xml.Attr nor xml.CharData. 115 // Note: in original node-tree parser, val defaulted to ""; 116 // so we always had the default if len(node.nodes) == 0. 117 if len(na) > 0 { 118 n[skey] = na 119 } else { 120 n[skey] = "" // empty element 121 } 122 } 123 return n, nil 124 case xml.CharData: 125 // clean up possible noise 126 tt := strings.Trim(string(value), "\t\r\b\n ") 127 if len(tt) > 0 { 128 if len(na) > 0 { 129 na["#text"] = cast(tt, r) 130 } else if skey != "" { 131 n[skey] = cast(tt, r) 132 } else { 133 // per Adrian (http://www.adrianlungu.com/) catch stray text 134 // in decoder stream - 135 // https://github.com/clbanning/mxj/pull/14#issuecomment-182816374 136 // NOTE: CharSetReader must be set to non-UTF-8 CharSet or you'll get 137 // a p.Token() decoding error when the BOM is UTF-16 or UTF-32. 138 continue 139 } 140 } 141 default: 142 // noop 143 } 144 } 145 } 146 147 var castNanInf bool 148 149 // Cast "Nan", "Inf", "-Inf" XML values to 'float64'. 150 // By default, these values will be decoded as 'string'. 151 func CastNanInf(b bool) { 152 castNanInf = b 153 } 154 155 // cast - try to cast string values to bool or float64 156 func cast(s string, r bool) interface{} { 157 if r { 158 // handle nan and inf 159 if !castNanInf { 160 switch strings.ToLower(s) { 161 case "nan", "inf", "-inf": 162 return interface{}(s) 163 } 164 } 165 166 // handle numeric strings ahead of boolean 167 if f, err := strconv.ParseFloat(s, 64); err == nil { 168 return interface{}(f) 169 } 170 // ParseBool treats "1"==true & "0"==false 171 // but be more strick - only allow TRUE, True, true, FALSE, False, false 172 if s != "t" && s != "T" && s != "f" && s != "F" { 173 if b, err := strconv.ParseBool(s); err == nil { 174 return interface{}(b) 175 } 176 } 177 } 178 return interface{}(s) 179 }