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  }