Golang Data Structures Chapter 5.2
Index of GoLang Resources
- How to install Go? | Chapter 1 |
- Creating Your First GoLang Project |Chapter 2 |
- GoLang Language Fundamentals | Chapter 3 |
- Control Flow in GoLang | Chapter 4 |
- GoLang Data Structures | Chapter 5.1 |
- GoLang Data Structures | Chapter 5.2 |
- GoLang Concurrency | Chapter 6 |
- Error Handling in GoLang | Chapter 7 |
- Common Utilities in GoLang Project | Chapter 8 |
- GoLang Database/SQL | Chapter 9 |
- GO with GORM | Chapter 10 |
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 a 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 –
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
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 the 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 a 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 the 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 reflecting. StructTag, by convention the value of a tag string is a space-separated key:”value” pairs,
Example –
If you want to send some field value empty then write “omitempty” flag in order to let JSON parser know.
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
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
If we see the source code, the NewEncoder(w *io.Writer) *Encoder takes the writer and returns the pointer to an Encoder for encoding and os.Stdout is open file pointing to standard input. Stdout is a pointer to file and a 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)
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)
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 the language more clear and understandable.
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 a value of an interface type, you don’t know only what it is; you only know what it can do.
Interfaces have named a collection of method signatures only (like java). To implement interfaces in Go, we need to implement all the methods of that interface.
Like a struct, we can create an interface by specifying type keyword followed by its name and keyword interface that will contain methods.
Example –
Now anything that has the same method signature implements interface like –
In above sample example, area( ) associated with square struct has the same signature like shape interface area( ) that means shape interface is implemented by square struct.
See the below example,
This is what we call Polymorphism. Polymorphism is an ability to write code that takes on different behavior through its implementation of types.
When you are passing an interface value, then there is no guarantee whether interface type is or isn’t a pointer. In the above example, we created a 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
A 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.
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)
→ 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 a 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 that you just need to use the correct method signature to use from the interface.
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 an interface called type Interface. Sort package provides sorting of any sequence according to ordering function. Go’s Sort function assumes nothing about the representation of the sequence. Instead, it uses sort.interface for specifying sorting sequence and sorting algorithm.
An in-place algorithm needs 3 things – the length of sequence for comparing two elements and way to swap two elements- so it contains 3 methods.
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.
Empty Interface –
Empty interface has zero methods. It is like Object class (which is a 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{ }.
Conversion vs Assertion –
Conversion is converting to one type from another type like int to float. An 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 –
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)
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 the boolean value to tell whether this operation holds or not
O/P: Value is not string
This time o/p will be “value is not the string” instead of getting a 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‘