# Introduction to Go: JSON introduction to Go part 2 In programming languages, you will very often deal with data structures internally.
Sometimes, you need to pass data outside of your application or read data from another application, or even a file.
API, for example often expose data in `json`, `xml`, `grpc` and all sorts of formats. Before we can really take a look at building APIs and even storing data in databases, we need some fundamental knowledge about how to convert these data structures, into structures that our application can understand.
In part 1, we dealt with [Variables]("https://tour.golang.org/basics/8") and more importantly,
we dealt with `struct` data type, which made us build a `customer` object. If we wanted to pass the `customer` to a database, or an external system, it's often required that we convert this to `json` format. ## Dev Environment The same as Part 1, we start with a [dockerfile](./dockerfile) where we declare our version of `go`. ``` cd golang\introduction\part-2.json docker build --target dev . -t go docker run -it -v ${PWD}:/work go sh go version ``` ## Create our App Create a new directory that holds defines our `repository` and holds our `module` ``` mkdir videos ``` * Define a module path ``` # change directory to your application source code cd videos # create a go module file go mod init videos ``` ## Create our base code We start out all our applications with a `main.go` defining our `package`, declaring our `import` dependencies
and our entrypoint `main()` function ``` package main import "fmt" func main() { fmt.Println("Hello, world.") } ``` ## Create our Videos app Firstly, we create a seperate code file `videos.go` that deals with our YouTube videos
Similar to Part 1, we define the file, we define what a video looks like using a `struct`
and a function for returning a slice of videos. ``` package main import () type video struct { Id string Title string Description string Imageurl string Url string } func getVideos()(videos []video){ //Get our videos here, //and return them return videos } ``` ## Populating our Video Struct Similar to our customers app, we created a `struct` and populated it with data: Let's add the following to our `getVideos()` function: ``` video1 := video{ Id : "eyvLwK5C2dw", Title : "Kubernetes on Azure", Imageurl : "https://i.ytimg.com/vi/eyvLwK5C2dw/mqdefault.jpg?sqp=CISC_PoF&rs=AOn4CLDo7kizrJozB0pxBhxL9JbyiW_EPw", Url : "https://youtu.be/eyvLwK5C2dw", Description : "", } video2 := video{ Id : "QThadS3Soig", Title : "Kubernetes on Amazon", Imageurl : "https://i.ytimg.com/vi/QThadS3Soig/sddefault.jpg", Url : "https://youtu.be/QThadS3Soig", Description : "", } return []video{ video1, video2} ``` We need to also invoke our function that will return the videos Don't worry about where the videos will come from.
We always start with the building blocks and move on from there
In our `main()` function:
``` func main() { videos := getVideos() fmt.Println(videos) } ``` ## Files Now, Ideally, we do not want to "hard code" our videos like this.
Currently, our videos are embedded into the code and we can only return 2 videos.
If we want to introduce a new video, we have to rebuild the application.
In the real world, videos are our data.
Data lives outside of the application. Like in a database. Technically, we can use a database, but that is too big of a step for now.
So let's start with a file instead. ### Introducing ioutil It's important to learn how to navigate and read go documentation. Let's go to https://golang.org/pkg/io/ioutil/ We can import this library since its part of the Go standard library set. ``` import ( "io/ioutil" ) ``` Let's move our videos into a `json` file called `videos.json` : ``` [ { "id" : "QThadS3Soig", "title" : "Kubernetes on Amazon", "imageurl" : "https://i.ytimg.com/vi/QThadS3Soig/sddefault.jpg", "url" : "https://youtu.be/QThadS3Soig", "description" : "" }, { "id" : "eyvLwK5C2dw", "title" : "Kubernetes on Azure", "imageurl" : "https://i.ytimg.com/vi/eyvLwK5C2dw/mqdefault.jpg?sqp=CISC_PoF&rs=AOn4CLDo7kizrJozB0pxBhxL9JbyiW_EPw", "url" : "https://youtu.be/eyvLwK5C2dw", "description" : "" } ] ``` ## Reading a file Let's take another look at https://golang.org/pkg/io/ioutil/
Notice the `ReadFile` function We can read a file from disk: ``` fileBytes, err := ioutil.ReadFile("./videos.json") if err != nil { panic(err) } fileContent := string(fileBytes) fmt.Println(fileContent) ``` ## Panic Also notice the function `panic`.
"A panic typically means something went unexpectedly wrong. Mostly we use it to fail fast on errors that shouldn’t occur during normal operation, or that we aren’t prepared to handle gracefully." - https://gobyexample.com/panic For now, we will panic on every potential error.
In a future video, we'll cover Error handling in more depth ## JSON Working with JSON is pretty straightforward in go.
`struct` objects are pretty well synergized with `json` Let's take a look at the `json` package that is part of go. https://golang.org/pkg/encoding/json/ ``` "encoding/json" ``` This package allows us to marshal and unmarshal JSON.
This allows conversion between a `struct` or a go type, and `json` ## Unmarshal From JSON(bytes) ==> Struct\Go type ``` err = json.Unmarshal(fileBytes, &videos) if err != nil { panic(err) } return videos ``` ## Using our Data Now in the real world, you would use your data to do something.
Let's say we'd like to update some common terms and conditions to the video description
In our `main` function, let's loop the videos & update the description. Feel free to checkout Part 1 of the series on `loops`! ``` for _, video := range videos { } ``` Now we dont want to override the description, we want to inject the terms and conditions on a new line ``` video.Description = video.Description + "\n Remember to Like & Subscribe!" ``` Note that when we run this, it does not print our new description field
This is because the loop `range` is giving us a copy of the video object.
This means we are updating a copy, but printing out the original.
We need to use the loop indexer and update the original object in the slice.
``` for i, _ := range videos { videos[i].Description = videos[i].Description + "\n Remember to Like & Subscribe!" } ``` Run our app again and now it updates the original video! ## Marshal https://golang.org/pkg/encoding/json/#Marshal From Struct\Go type ==> JSON(bytes) Let's create a new function for saving our video back to file ``` func saveVideos(videos []video)(){ videoBytes, err := json.Marshal(videos) if err != nil { panic(err) } } ``` ## Writing to File https://golang.org/pkg/io/ioutil/ ``` err = ioutil.WriteFile("./videos-updated.json", videoBytes, 0644) if err != nil { panic(err) } ``` Then in our `main` function we can save our videos after the update: ``` saveVideos(videos) ``` ## Mapping fields In our example our `json` and our struct fields match exactly. Sometimes this is not possible to maintain. Sometimes we need to tell go, to map certain `json` fields to certain fields in our `struct` We can do it like so: ``` type video struct { Id string `json:"id"` Title string `json:"title"` Description string `json:"description"` Imageurl string `json:"imageurl"` Url string `json:"url"` } ```