...

Source file src/github.com/aws/smithy-go/encoding/xml/value.go

Documentation: github.com/aws/smithy-go/encoding/xml

     1  package xml
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  	"math/big"
     7  	"strconv"
     8  
     9  	"github.com/aws/smithy-go/encoding"
    10  )
    11  
    12  // Value represents an XML Value type
    13  // XML Value types: Object, Array, Map, String, Number, Boolean.
    14  type Value struct {
    15  	w       writer
    16  	scratch *[]byte
    17  
    18  	// xml start element is the associated start element for the Value
    19  	startElement StartElement
    20  
    21  	// indicates if the Value represents a flattened shape
    22  	isFlattened bool
    23  }
    24  
    25  // newFlattenedValue returns a Value encoder. newFlattenedValue does NOT write the start element tag
    26  func newFlattenedValue(w writer, scratch *[]byte, startElement StartElement) Value {
    27  	return Value{
    28  		w:            w,
    29  		scratch:      scratch,
    30  		startElement: startElement,
    31  	}
    32  }
    33  
    34  // newValue writes the start element xml tag and returns a Value
    35  func newValue(w writer, scratch *[]byte, startElement StartElement) Value {
    36  	writeStartElement(w, startElement)
    37  	return Value{w: w, scratch: scratch, startElement: startElement}
    38  }
    39  
    40  // writeStartElement takes in a start element and writes it.
    41  // It handles namespace, attributes in start element.
    42  func writeStartElement(w writer, el StartElement) error {
    43  	if el.isZero() {
    44  		return fmt.Errorf("xml start element cannot be nil")
    45  	}
    46  
    47  	w.WriteRune(leftAngleBracket)
    48  
    49  	if len(el.Name.Space) != 0 {
    50  		escapeString(w, el.Name.Space)
    51  		w.WriteRune(colon)
    52  	}
    53  	escapeString(w, el.Name.Local)
    54  	for _, attr := range el.Attr {
    55  		w.WriteRune(' ')
    56  		writeAttribute(w, &attr)
    57  	}
    58  
    59  	w.WriteRune(rightAngleBracket)
    60  	return nil
    61  }
    62  
    63  // writeAttribute writes an attribute from a provided Attribute
    64  // For a namespace attribute, the attr.Name.Space must be defined as "xmlns".
    65  // https://www.w3.org/TR/REC-xml-names/#NT-DefaultAttName
    66  func writeAttribute(w writer, attr *Attr) {
    67  	// if local, space both are not empty
    68  	if len(attr.Name.Space) != 0 && len(attr.Name.Local) != 0 {
    69  		escapeString(w, attr.Name.Space)
    70  		w.WriteRune(colon)
    71  	}
    72  
    73  	// if prefix is empty, the default `xmlns` space should be used as prefix.
    74  	if len(attr.Name.Local) == 0 {
    75  		attr.Name.Local = attr.Name.Space
    76  	}
    77  
    78  	escapeString(w, attr.Name.Local)
    79  	w.WriteRune(equals)
    80  	w.WriteRune(quote)
    81  	escapeString(w, attr.Value)
    82  	w.WriteRune(quote)
    83  }
    84  
    85  // writeEndElement takes in a end element and writes it.
    86  func writeEndElement(w writer, el EndElement) error {
    87  	if el.isZero() {
    88  		return fmt.Errorf("xml end element cannot be nil")
    89  	}
    90  
    91  	w.WriteRune(leftAngleBracket)
    92  	w.WriteRune(forwardSlash)
    93  
    94  	if len(el.Name.Space) != 0 {
    95  		escapeString(w, el.Name.Space)
    96  		w.WriteRune(colon)
    97  	}
    98  	escapeString(w, el.Name.Local)
    99  	w.WriteRune(rightAngleBracket)
   100  
   101  	return nil
   102  }
   103  
   104  // String encodes v as a XML string.
   105  // It will auto close the parent xml element tag.
   106  func (xv Value) String(v string) {
   107  	escapeString(xv.w, v)
   108  	xv.Close()
   109  }
   110  
   111  // Byte encodes v as a XML number.
   112  // It will auto close the parent xml element tag.
   113  func (xv Value) Byte(v int8) {
   114  	xv.Long(int64(v))
   115  }
   116  
   117  // Short encodes v as a XML number.
   118  // It will auto close the parent xml element tag.
   119  func (xv Value) Short(v int16) {
   120  	xv.Long(int64(v))
   121  }
   122  
   123  // Integer encodes v as a XML number.
   124  // It will auto close the parent xml element tag.
   125  func (xv Value) Integer(v int32) {
   126  	xv.Long(int64(v))
   127  }
   128  
   129  // Long encodes v as a XML number.
   130  // It will auto close the parent xml element tag.
   131  func (xv Value) Long(v int64) {
   132  	*xv.scratch = strconv.AppendInt((*xv.scratch)[:0], v, 10)
   133  	xv.w.Write(*xv.scratch)
   134  
   135  	xv.Close()
   136  }
   137  
   138  // Float encodes v as a XML number.
   139  // It will auto close the parent xml element tag.
   140  func (xv Value) Float(v float32) {
   141  	xv.float(float64(v), 32)
   142  	xv.Close()
   143  }
   144  
   145  // Double encodes v as a XML number.
   146  // It will auto close the parent xml element tag.
   147  func (xv Value) Double(v float64) {
   148  	xv.float(v, 64)
   149  	xv.Close()
   150  }
   151  
   152  func (xv Value) float(v float64, bits int) {
   153  	*xv.scratch = encoding.EncodeFloat((*xv.scratch)[:0], v, bits)
   154  	xv.w.Write(*xv.scratch)
   155  }
   156  
   157  // Boolean encodes v as a XML boolean.
   158  // It will auto close the parent xml element tag.
   159  func (xv Value) Boolean(v bool) {
   160  	*xv.scratch = strconv.AppendBool((*xv.scratch)[:0], v)
   161  	xv.w.Write(*xv.scratch)
   162  
   163  	xv.Close()
   164  }
   165  
   166  // Base64EncodeBytes writes v as a base64 value in XML string.
   167  // It will auto close the parent xml element tag.
   168  func (xv Value) Base64EncodeBytes(v []byte) {
   169  	encodeByteSlice(xv.w, (*xv.scratch)[:0], v)
   170  	xv.Close()
   171  }
   172  
   173  // BigInteger encodes v big.Int as XML value.
   174  // It will auto close the parent xml element tag.
   175  func (xv Value) BigInteger(v *big.Int) {
   176  	xv.w.Write([]byte(v.Text(10)))
   177  	xv.Close()
   178  }
   179  
   180  // BigDecimal encodes v big.Float as XML value.
   181  // It will auto close the parent xml element tag.
   182  func (xv Value) BigDecimal(v *big.Float) {
   183  	if i, accuracy := v.Int64(); accuracy == big.Exact {
   184  		xv.Long(i)
   185  		return
   186  	}
   187  
   188  	xv.w.Write([]byte(v.Text('e', -1)))
   189  	xv.Close()
   190  }
   191  
   192  // Write writes v directly to the xml document
   193  // if escapeXMLText is set to true, write will escape text.
   194  // It will auto close the parent xml element tag.
   195  func (xv Value) Write(v []byte, escapeXMLText bool) {
   196  	// escape and write xml text
   197  	if escapeXMLText {
   198  		escapeText(xv.w, v)
   199  	} else {
   200  		// write xml directly
   201  		xv.w.Write(v)
   202  	}
   203  
   204  	xv.Close()
   205  }
   206  
   207  // MemberElement does member element encoding. It returns a Value.
   208  // Member Element method should be used for all shapes except flattened shapes.
   209  //
   210  // A call to MemberElement will write nested element tags directly using the
   211  // provided start element. The value returned by MemberElement should be closed.
   212  func (xv Value) MemberElement(element StartElement) Value {
   213  	return newValue(xv.w, xv.scratch, element)
   214  }
   215  
   216  // FlattenedElement returns flattened element encoding. It returns a Value.
   217  // This method should be used for flattened shapes.
   218  //
   219  // Unlike MemberElement, flattened element will NOT write element tags
   220  // directly for the associated start element.
   221  //
   222  // The value returned by the FlattenedElement does not need to be closed.
   223  func (xv Value) FlattenedElement(element StartElement) Value {
   224  	v := newFlattenedValue(xv.w, xv.scratch, element)
   225  	v.isFlattened = true
   226  	return v
   227  }
   228  
   229  // Array returns an array encoder. By default, the members of array are
   230  // wrapped with `<member>` element tag.
   231  // If value is marked as flattened, the start element is used to wrap the members instead of
   232  // the `<member>` element.
   233  func (xv Value) Array() *Array {
   234  	return newArray(xv.w, xv.scratch, arrayMemberWrapper, xv.startElement, xv.isFlattened)
   235  }
   236  
   237  /*
   238  ArrayWithCustomName returns an array encoder.
   239  
   240  It takes named start element as an argument, the named start element will used to wrap xml array entries.
   241  for eg, `<someList><customName>entry1</customName></someList>`
   242  Here `customName` named start element will be wrapped on each array member.
   243  */
   244  func (xv Value) ArrayWithCustomName(element StartElement) *Array {
   245  	return newArray(xv.w, xv.scratch, element, xv.startElement, xv.isFlattened)
   246  }
   247  
   248  /*
   249  Map returns a map encoder. By default, the map entries are
   250  wrapped with `<entry>` element tag.
   251  
   252  If value is marked as flattened, the start element is used to wrap the entry instead of
   253  the `<member>` element.
   254  */
   255  func (xv Value) Map() *Map {
   256  	// flattened map
   257  	if xv.isFlattened {
   258  		return newFlattenedMap(xv.w, xv.scratch, xv.startElement)
   259  	}
   260  
   261  	// un-flattened map
   262  	return newMap(xv.w, xv.scratch)
   263  }
   264  
   265  // encodeByteSlice is modified copy of json encoder's encodeByteSlice.
   266  // It is used to base64 encode a byte slice.
   267  func encodeByteSlice(w writer, scratch []byte, v []byte) {
   268  	if v == nil {
   269  		return
   270  	}
   271  
   272  	encodedLen := base64.StdEncoding.EncodedLen(len(v))
   273  	if encodedLen <= len(scratch) {
   274  		// If the encoded bytes fit in e.scratch, avoid an extra
   275  		// allocation and use the cheaper Encoding.Encode.
   276  		dst := scratch[:encodedLen]
   277  		base64.StdEncoding.Encode(dst, v)
   278  		w.Write(dst)
   279  	} else if encodedLen <= 1024 {
   280  		// The encoded bytes are short enough to allocate for, and
   281  		// Encoding.Encode is still cheaper.
   282  		dst := make([]byte, encodedLen)
   283  		base64.StdEncoding.Encode(dst, v)
   284  		w.Write(dst)
   285  	} else {
   286  		// The encoded bytes are too long to cheaply allocate, and
   287  		// Encoding.Encode is no longer noticeably cheaper.
   288  		enc := base64.NewEncoder(base64.StdEncoding, w)
   289  		enc.Write(v)
   290  		enc.Close()
   291  	}
   292  }
   293  
   294  // IsFlattened returns true if value is for flattened shape.
   295  func (xv Value) IsFlattened() bool {
   296  	return xv.isFlattened
   297  }
   298  
   299  // Close closes the value.
   300  func (xv Value) Close() {
   301  	writeEndElement(xv.w, xv.startElement.End())
   302  }
   303  

View as plain text