Go Data Structures

As we have explored major Data Structures in Part I of  Fifth Chapter,  let’s dive deeper in Part II –

JSON

JSON(JavaScript Object Notation) is simple text based data interchanging format. Basically we use structs for JSON for putting and receiving the data. It is lightweight and gives faster performance to access the data

Encoding –

  • Marshal → to encode GO values to JSON in string format
  • Unmarshal → to decode JSON data to GO values
  • Encoding → same as Marshal but in streams
  • Decoding → same as unmarshal but from streams
Go has built-in support for JSON encoding by providing functions in “json/encoding” package.

Marshalling

Marshalling converts the Golang object to JSON encoding into strings. Golang has provided marshal( ) function for this –
func Marshal( v interface { }) ([ ]byte,error)
This function will convert the given data structure into JSON in [ ]byte format which we need to convert into string.
Example – Given data structure Person
type Person struct{
 Name string
 Address string
 age int /* this field will not be converted into JSON as 
         should start with capital letter */
}
Create an instance of Person
per:=Person{“George”,”United Kingdom”,25}
Now, we can marshal encoded JSON using marshal( ) function.But dont forget to import the “encoding/json” package.
B,err := json.Marshal(per)
If marshalling happens, then err will be nil and b will be [ ] byte containing JSON data like below.
b==[ ]byte {`{“Name”:”George”,”Address”:”United Kingdom”}`}
Here, as per above struct age field will not come because it is not exported which is mandatory for all fields.
Now, we need to convert this [ ]byte to string. So do the type casting
string(b)
Now, JSON will be like {“Name”:”George”,”Address”:”United Kingdom”}
Points:
  • All fields should start with capital letter so that they will be exported
  • JSON objects only support strings as keys; if you want to encode map
    type to JSON then it must be in map[string] T
  • There is no way for encoding of channels, function types into JSON
  • Cyclic data structures are not supported
  • Pointers are dereferenced before encoded into JSON

Unmarshalling

Unmarshalling converts the JSON data to GO objects. Golang has provided unmarshal( ) function for this –
func Unmarshal(data [ ]byte,v interface{ })
Here, we specify JSON text as string and converts into [ ]byte slice
Now, first create the place of that struct type where decoded data will be stored.
var per Person
Now call json.Unmarshal( ) by passing it a []byte of JSON data and a pointer to per.
err:=json.Unmarshal(b,&per)
If the JSON fits and matches with struct fields, after the call err will be nil and data from b will have been stored in the struct m, but non-matched fields will not be decoded
String keys in JSON must be matched to struct fields.

Tags

The tags are given to the fields to attach meta information and acquired by using reflection. It is used to provide info on how struct field is encoded to or decoded from another format (or stored /retrieved from database), but you can use it to store whatever meta-info you want either for another package or for your use.
As we see in the documentation of reflect. StructTag, by convention the value of a tag string is a space-separated key:”value” pairs,
Example –
type User struct{
 Name string ` json:”name” xml:”name” ` //tag in backticks
}
If you want to send some field value empty then write “omitempty” flag in order to let JSON parser know.
type User struct{
 Name string ` json:”name” xml:”name” ` 
 Age int `json:”omitempty”`
}
With omitempty JSON value will be { }
We can use reflection(reflect package) to access the tag values of struct fields. We need to acquire the type of struct and then we can give query to fields e.g. Type.Field(i int) / Type.FieldbyName(name string)
This will return value of struct field.
The commonly used tag keys are →
  • json → used by json.encoding package, detailed at json.Marshal()
  • xml → used by encoding/xml, detailed at xml.Marshal()
  • yaml → used by gopkg.in/yaml.v2 package, at yaml.Marshal()
  • bson→ used by gobson package, detailed at bson.Marshal()
  • Other – orm, db, gorm, datastore, schema, asn
package main
import (
 "encoding/json"
 "fmt"
)
type person struct {
 Firstname string
 Lastname string
 Age int `json:"Umar"` //tag in backticks 
 notexported int //will not be exported
}
func main() {
 p1 := person{"Rob", "Pike", 24, 007}
 bs1, _ := json.Marshal(p1) // Marshalling
 fmt.Println(string(bs1))
 bs := []byte(bs1)
 json.Unmarshal(bs, p1) //Unmarshalling
 fmt.Println(bs)
}

Encoding

Encoding works with writer and writing streams of JSON data. The GO has provided below function for encoding JSON through writer interface –
func (enc *Encoder) Encode (v interface{ }) error
json.NewEncoder(os.Stdout).Encode(p1)
If we see the source code, the NewEncoder(w *io.Writer) *Encoder takes writer and returns pointer to an Encoder for encoding and os.Stdout are open files pointing to standard input. Stdout is a pointer to file and pointer to a file implements func(f *file ) Write(b [ ]byte) (n int,err error) and that means it is implementing this method from Writer interface. (Polymorphism)
main.go
 package main
import (
 "encoding/json"
 "fmt"
 "os"
)
type person struct {
 Name string
 Age int
}
func main() {
 p1 := person{"George", 23}
 json.NewEncoder(os.Stdout).Encode(p1) /* Encoding through Streams */
 fmt.Println(p1.Name, "--", p1.Age)
}

Decoding

Decoding works with reader and reading streams of JSON data. The GO has provided below function for encoding JSON through writer interface –
func (dec *Decoder) Decode (v interface{ }) error
NewReader( ) will return a reader and give it to NewDecoder( ) so that it will decode the JSON.
json.NewDecoder(reader).Decode(&p1)
main.go
package main
import (
 "encoding/json"
 "fmt"
 "strings"
)
type person struct {
 Name string
 Age int
 address int //this field will not be exported
}
func main() {
 var p1 person
 reader := strings.NewReader(`{"Name":"James", "Age":24, 
"address":"New York"}`) // JSON
 json.NewDecoder(reader).Decode(&p1)
 fmt.Println(p1.Name)
 fmt.Println(p1.Age)
 fmt.Println(p1.address)
}

Strings

Golang’s standard library has provided built-in functions to deal with strings in the “strings” package.
“Computer” → This string has 8 runes. So no need of counting for finding its length as there is built-in function len( ) for calculating its length.
In Golang, we use len( ) for slice, array, map for measuring their length. So this makes language more clear and understandable.
func main(){
 var s string=”Computer”
 fmt.Println(len(s)) 
 }
main.go
 package main
import "fmt"
import s "strings" //we can also give prefixes and use
import "strings" //also we can use normally
var b = fmt.Println //prefix for fmt.Println()
func main() {
 var str string = "Computer-Security"
 fmt.Println(len(str)) //length: 18
 fmt.Println("Contains:", s.Contains(str, "ecu")) //true
 fmt.Println("Compare:", s.Compare(str, "Computer Security")) 
/*return 0 if a==b,-1 if a<b,+1 if a>b*/
 fmt.Println("Count", strings.Count(str, "e")) //returns 2
 b("Index:", s.Index(str, "m")) // returns 2
 b("ToLower", strings.ToLower(str))
 b("ToUpper", strings.ToUpper(str))
 b("Replace", s.Replace(str, "e", "i", 2)) /* replace e by i for 2 Occurrences */
 b("Split", strings.Split(str, "-"))
}

Interface

An interface is an abstract type. It doesn’t expose the representation or internal structure of its values, or set of basic operation they support; it reveals only some of their methods. When you have value of an interface type, you don’t know only what it is; you only know what it can do.
Interfaces are named collection of method signatures only (like java). To implement an interfaces in Go, we need to implement all the methods of that interface.
Like a struct we can create interface by specifying type keyword followed by its name and keyword interface that will contain methods.
type interface_name interface{
 /* method signature (one or more method sets) */
}
Example –
type shape interface{
 area( ) float64 //method
} /* interface creation with method declaration
Now anything that has same method signature implements interface like –
type square struct { 
 side float64
 }

 func (sq square) area( ) float64 { //area( ) associated with square struct
 Return sq.side * sq.side
 }
In above sample example, area( ) associated with square struct has same signature like shape interface area( ) that means shape interface is implemented by square struct.
See the below example,
shapeI.go
 package IShape
type Shape interface {
 Area() float64
}
CircleI.go
package SCircle
import ("math")
type Circle struct {
 Radius float64
}
func (c Circle) Area() float64 { //Here, Area( )is implemented
 return math.Pi * c.Radius * c.Radius
}
SquareI.go
package SSquare
type Square struct {
 Side float64
}
func (s Square) Area() float64 { //Here, Area( )is implemented

 return s.Side * s.Side
}
main.go
package main
import (
 "fmt"
 "interface/basic/IShape" /* These are packages I’ve created */
 "interface/basic/SCircle"
 "interface/basic/SSquare"
)
func info(s IShape.Shape) { /* Now here info( ) will accept dynamically
 any of type of square or circle because 
 Shape is implemented by both of them*/
 fmt.Println(s)
 fmt.Println(s.Area())

}
func main() {
 s := SSquare.Square{10}
 info(s) /* Here, I’ve passed square type */
 c := SCircle.Circle{7}
 info(c) /* Here, I’ve passed circle type */
}
This is what we call Polymorphism. Polymorphism is ability to write code that take on different behaviour through its implementation of types.
When you are passing an interface value,then there is no guarantee whether interface type is or isn’t pointer. In the above example, we created function based on value receiver and we passed associated values of square or circle to it.
Just take method with pointer receiver.
func (c *Circle) Area() float64 {
 }
Now if we try to pass interface value to it, it will give an error
Circle does not implement Shape(Area method requires pointer receiver). So for the pointer receivers we need to pass the *Circle pointer to the Shape instead of a Circle value, by using new(Circle) or &Circle.

Remember :

Interfaces are types that just declare the behavior. This behavior is never implemented by interface directly, but instead by user defined types; values of user defined type can be assigned to values & interface type. This assignment stores values of user defined type into interface value. – Bill Kenedy
Interfaces can also be used as fields.
type struct MultiShape{
 shapes [ ]Shape
 }
If you see “bufio” and “ioutil” package in Go’s language specification, you’ll get something like this –
func NewScanner(r io.Reader) *Scanner
→ bufio.NewScanner(res.Body)
→ In the Body interface, ReadCloser interface is implemented which again implements Reader interface that means Indirectly we are dealing with the Reader.
func ReadAll(r io.Reader) ([]byte, error)
→ ioutil.ReadAll(res.Body)
→ Same as above
func NewReader(rd io.Reader) *Reader
We need to define meaningful name & type for fields or properties. We can initialize fields with its value or can keep blank so it will use default value.
In other languages (like Java), we have to explicitly say that this interface is implemented by other. But in Go interfaces are implemented implicitly so you don’t need to say like that you just need to use the correct method signature to use from interface.
type ReaderWriter {
Reader
Writer
} /* This is called embedding of interfaces */
A type satisfies an interface if it possesses all methods that interface requires. Conceptually, value of interface type or interface value has two components – A concrete type and concrete value.

Sort Package

  • type Interface

“Sort” package has interface called type Interface. Sort package provides sorting of any sequence according to ordering function. Go’s Sort function assumes nothing about representation of sequence. Instead, it uses sort.interface for specifying sorting sequence and sorting algorithm.
An in-place algorithm needs 3 things – length of sequence for comparing two elements and way to swap two elements- so it contains 3 methods.
package sort
type Interface interface{
 Len( ) int
 Less(i,j int) bool
 Swap(i,j int) /* Method sets */
 }
So if you want to use sorting then we need to implement these methods from sort.Interface package and then apply Sort( ) on it.
Package sort provided primitives for sorting slices and user defined collection.
main.go
 package main
import (
 "fmt"
 "sort"
)
type Person struct {
 Name string
 Age int
}

type ByAge []Person /*This type of []Person has implemented sort.Interface interface*/
func (b ByAge) Len() int {
 return len(b)
}
func (b ByAge) Swap(i, j int) {
 b[i], b[j] = b[j], b[i]
}
func (b ByAge) Less(i, j int) bool {
 return b[i].Age > b[j].Age
}
func main() {
 persons := []Person{
 {"George", 34},
 {"Robin", 43},
 {"Stuart", 23}}
 peoples := []string{"Keyboard", "mouse", "Computer"}
 rev := []string{"Book", "Literature", "Novel"}
 ints := []int{54, 76, 33, 12, 54, 76}
 fmt.Println("Before Sort:", persons)
 fmt.Println("Before Sort:", peoples)
 sort.Strings(peoples) /* Sorting string slice */
 sort.Sort(sort.Reverse(sort.StringSlice(rev)))
 fmt.Println("Reversed String Slice", rev)
 sort.Sort(sort.IntSlice(ints))
 fmt.Println("Sorted Integer value", ints)
 fmt.Println("After Sort:", peoples)
 sort.Sort(ByAge(persons))
 fmt.Println("After Sort:", persons)
}

Empty Interface

Empty interface has zero methods. It is like Object class (which is superclass of all classes in java and accept any object). Similar to this, empty interface accepts any interface to itself.
An empty interface can hold any hold values of any type.
Empty interfaces are used by code that handles values of any type.
For example, fmt.Println(a …interface{ }) (n int,err error) takes any type number of arguments of type interface{ }.
main.go
package main
import (
 "fmt"
)
type vehicles interface{ } //Empty interface
type vehicle struct {
 speed string
 color string
}
type car struct {
 vehicle
 wheels int
 doors int
}
type plane struct {
 vehicle
 jet bool
}
type boat struct {
 vehicle
 capacity int
}
func specifications(veh interface{}) { /* accepting anything*/
 fmt.Println(veh)
}
func main() {
 audiR8 := car{vehicle{"240 KMPH", "White"}, 4, 4} 
 benz := car{vehicle{"250 KMPH", "Black"}, 4, 4}
 boeing := plane{vehicle{"500 KMPH", "White"}, true}
 sanger := boat{vehicle{"350 KMPH", "Saffron"}, 10000}
 travelling := []vehicles{audiR8, benz, boeing, sanger}
 for key, value := range travelling {
 fmt.Println(key, "-", value)
 }
 specifications(audiR8)
 specifications(benz)
 specifications(boeing)
 specifications(sanger)
}

Conversion vs Assertion

Conversion is converting to one type from another type like int to float. Assertion is different from conversion.
Conversion process deals with two concepts like –
  • Widening → Converting from Lower to Higher data type
  • Narrowing → Converting from Higher to Lower data type 
Example –
var x=15 //int type
var y=15.45
fmt.Println(float(x)) → 15.00
fmt.Println(int(y)) → 15
var x rune=’a’
var y int32=’b’
fmt.Println(x)
fmt.Println(y)
fmt.Println(string(x)) →a
fmt.Println(string(y)) →b
Now come to assertion, sometimes you want to know the exact type of interface variable, in this case you have to use type assertion.
interface_variable.(type)
main.go
package main
import “fmt”
func display(v interface{ }){
fmt.Printf(“Value is:%v”,v.(int)) /*assert or test the correct va*/
}
func main(){
 display(10)
}
O/P: 10
But here, if we put v.(string) then we will get running panic error
panic: interface conversion: interface {} is int, not string
goroutine 1 [running]:
main.display(0x497e00, 0xc0420361c0)
For this, assertion actually returns boolean value to tell whether this operation holds or not
main.go
 package main
import “fmt”
func display(v interface{ }){
 if v,ok:=v.(string);ok {
fmt.Printf(“Value is:%v”,v.(int)) /*assert/test the correct val*/
}else{
fmt.Println(“Value is not string”)
}
}
func main(){
 display(10)
}
O/P: Value is not string
This time o/p will be “value is not string” instead of getting panic error. We can also use type switch
As we have seen ‘Data Structures’ in this chapter, let’s move to the next one and see ‘Concurrency in Golang Project‘
Related Posts
Comments
  • WINlar

    You know if you have wisdom teeth by examining your mouth and finding three permanent molars in each dental quadrant. However, if the tooth is impacted under the Which teeth are your wisdom teeth

Leave a Comment

golang-error-handlinggolang-concurrency