github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/json/encode.gno (about) 1 package json 2 3 import ( 4 "bytes" 5 "errors" 6 "math" 7 "strconv" 8 9 "gno.land/p/demo/json/ryu" 10 "gno.land/p/demo/ufmt" 11 ) 12 13 // Marshal returns the JSON encoding of a Node. 14 func Marshal(node *Node) ([]byte, error) { 15 var ( 16 buf bytes.Buffer 17 sVal string 18 bVal bool 19 nVal float64 20 oVal []byte 21 err error 22 ) 23 24 if node == nil { 25 return nil, errors.New("node is nil") 26 } 27 28 if !node.modified && !node.ready() { 29 return nil, errors.New("node is not ready") 30 } 31 32 if !node.modified && node.ready() { 33 buf.Write(node.source()) 34 } 35 36 if node.modified { 37 switch node.nodeType { 38 case Null: 39 buf.Write(nullLiteral) 40 41 case Number: 42 nVal, err = node.GetNumeric() 43 if err != nil { 44 return nil, err 45 } 46 47 // ufmt does not support %g. by doing so, we need to check if the number is an integer 48 // after then, apply the correct format for each float and integer numbers. 49 if math.Mod(nVal, 1.0) == 0 { 50 // must convert float to integer. otherwise it will be overflowed. 51 num := ufmt.Sprintf("%d", int(nVal)) 52 buf.WriteString(num) 53 } else { 54 // use ryu algorithm to convert float to string 55 num := ryu.FormatFloat64(nVal) 56 buf.WriteString(num) 57 } 58 59 case String: 60 sVal, err = node.GetString() 61 if err != nil { 62 return nil, err 63 } 64 65 quoted := ufmt.Sprintf("%s", strconv.Quote(sVal)) 66 buf.WriteString(quoted) 67 68 case Boolean: 69 bVal, err = node.GetBool() 70 if err != nil { 71 return nil, err 72 } 73 74 bStr := ufmt.Sprintf("%t", bVal) 75 buf.WriteString(bStr) 76 77 case Array: 78 buf.WriteByte(bracketOpen) 79 80 for i := 0; i < len(node.next); i++ { 81 if i != 0 { 82 buf.WriteByte(comma) 83 } 84 85 elem, ok := node.next[strconv.Itoa(i)] 86 if !ok { 87 return nil, ufmt.Errorf("array element %d is not found", i) 88 } 89 90 oVal, err = Marshal(elem) 91 if err != nil { 92 return nil, err 93 } 94 95 buf.Write(oVal) 96 } 97 98 buf.WriteByte(bracketClose) 99 100 case Object: 101 buf.WriteByte(curlyOpen) 102 103 bVal = false 104 for k, v := range node.next { 105 if bVal { 106 buf.WriteByte(comma) 107 } else { 108 bVal = true 109 } 110 111 key := ufmt.Sprintf("%s", strconv.Quote(k)) 112 buf.WriteString(key) 113 buf.WriteByte(colon) 114 115 oVal, err = Marshal(v) 116 if err != nil { 117 return nil, err 118 } 119 120 buf.Write(oVal) 121 } 122 123 buf.WriteByte(curlyClose) 124 } 125 } 126 127 return buf.Bytes(), nil 128 }