Converting JSON with array - json

I have written following code to get array from JSON, and want to retrieve something like
[{"id":"id1","friendly":"friendly1"},{"id":"id2","friendly":"friendly2"}]
But it's empty:
[{"id":"","friendly":""},{"id":"","friendly":""}]
package main
import (
"encoding/json"
"fmt"
)
var input = `[
{
"not needed": "",
"_source": {
"id": "id1",
"friendly": "friendly1"
}
},
{
"_source": {
"id": "id2",
"friendly": "friendly2"
}
}]`
type source struct {
Id string `json:"id"`
Friendly string `json:"friendly"`
}
func main() {
result := make([]source, 0)
sources := []source{}
json.Unmarshal([]byte(input), &sources)
for _, n := range sources {
result = append(result, n)
}
out, _ := json.Marshal(result)
fmt.Println(string(out))
}

Try creating an another struct that have one field called Source of type source. In my example below I called this struct outer. Your input should be an array of outer and your result an array of source.
Something like this:
import (
"encoding/json"
"fmt"
)
var input = `[
{
"not needed": "",
"_source": {
"id": "id1",
"friendly": "friendly1"
}
},
{
"_source": {
"id": "id2",
"friendly": "friendly2"
}
}]`
type outer struct {
Source source `json:"_source"`
}
type source struct {
Id string `json:"id"`
Friendly string `json:"friendly"`
}
func main() {
result := make([]source, 0)
sources := []outer{}
json.Unmarshal([]byte(input), &sources)
for _, n := range sources {
result = append(result, n.Source)
}
out, _ := json.Marshal(result)
fmt.Println(string(out))
}```

Related

Convert DynamoDB JSON to AttributeValue, Go Object or Json

I am trying to convert simple DynamoDB Object string:
{
"Item": {
"Id": {
"S": "db31"
},
"CreateTime": {
"N": "1647882237618915000"
}
}
to either dynamodb.AttributeValue and then map to a go object (go type structure) or convert to a simple JSON go object.
I think, there are similar answers (1, 2, 3) in Java, but I didn't find a similar implementation in Golang.
You could create a struct type and use json.Unmarshal to unmarshal the JSON string like this:
package main
import (
"encoding/json"
"fmt"
"os"
)
type Record struct {
Item struct {
Id struct {
S string
}
CreateTime struct {
N string
}
}
}
func main() {
str := `{
"Item": {
"Id": {
"S": "db31"
},
"CreateTime": {
"N": "1647882237618915000"
}
}
}`
var record Record
if err := json.Unmarshal([]byte(str), &record); err != nil {
fmt.Fprintf(os.Stderr, "unmarshal failed: %v", err)
os.Exit(1)
}
fmt.Printf("%s %s", record.Item.Id.S, record.Item.CreateTime.N)
}
If you want a different approach, and want to transform the result into a structure that is different than the JSON, you could use a library like gjson.
Here is an example "flattening" the result into a simpler struct:
package main
import (
"fmt"
"github.com/tidwall/gjson"
)
type Record struct {
Id string
CreateTime string
}
func main() {
str := `{
"Item": {
"Id": {
"S": "db31"
},
"CreateTime": {
"N": "1647882237618915000"
}
}
}`
values := gjson.GetMany(str, "Item.Id.S", "Item.CreateTime.N")
record := Record{
Id: values[0].Str,
CreateTime: values[1].Str,
}
fmt.Printf("%s %s", record.Id, record.CreateTime)
}

How to connect JSON that is unmarshaled to an HTML page using golang?

I've reached the point where I have unmarshaled my JSON and I can print the data directly from the registry.go file. What I don't understand (and I'm new to golang and backend dev) is how to connect that data to my html page. I would very much appreciate if someone could explain to me how to do this.
I have seen some examples, but they are all in one file, I want to keep the JSON, HTML, and Go pages separate. The JSON is being pulled directly from an API, and there will be multiple HTML pages using that data.
Here is an example, very close to the JSON I'm using, it is being pulled from an API:
{
"count": 4,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
},
{
"id": 2,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
},
{
"id": 3,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
},
{
"id": 4,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
}
]
}
Here is the registry.go page I am using. I have a separate main.go page as I have other pages that have been created. I am trying to keep this piece separate, but if I cannot please tell me.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
// Results struct which contains
// an array of results
type Results struct {
Count int `json:"count"`
Results []Result `json:"results"`
}
// Results struct which contains the ID and description
type Result struct {
ID int `json:"id"`
Description string `json:"description"`
DisplayName string `json:"displayName"`
Status Status `json:"status"`
Type string `json:"type"`
}
// Status struct
type Result struct {
Status string `json:"status"`
LastUpdated string `json:"lastUpdated"`
}
func main() {
// Open the jsonFile
jsonFile, err := os.Open("test.json")
// if os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened test.json")
// defer the closing of the jsonFile in order to parse it later on
defer jsonFile.Close()
// read the opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(jsonFile)
// initialize the results array
var results Results
// unmarshal the byteArray
json.Unmarshal(byteValue, &results)
// Print the count
fmt.Println(results.count)
// iterate through the results and print
for i := 0; i < len(users.Users); i++ {
fmt.Println("ID: " + results.Results[i].ID)
fmt.Println("Desctiption: " + results.Results[i].Description)
}
}
And here is the html page I have created (test.html), just showing the body piece:
<body>
{{range .}}
<div>
<h3>{{.DisplayName}}</h3>
</div>
<div>{{.Id}}</div>
<div>{{.Description}}</div>
<div>{{.Type}}</div>
{{end}}
</body>
In Main.go I have the following references:
//handler to display the page
http.HandleFunc("/test", hndlerTest)
//hndlerTest - the Testing Page
func hndlerTest(w http.ResponseWriter, req *http.Request){
renderTemplate(w,"test.html", nil)
}
You can use the html template package to parse and render html filed and write the executed template to your http response. You can refer to the following example to start with.
package main
import (
"encoding/json"
"html/template"
"log"
"net/http"
)
var htmlFile = `
<body>
{{range .Foos}}
<span>Foo: {{.Foo}}</span>
{{end}}
</body>
`
var jsonData = `[{"foo":1},{"foo":2},{"foo":3}]`
type foo struct {
Foo int `json:"foo"`
}
type Data struct {
Foos []foo
}
func main() {
var d Data
if err := json.Unmarshal([]byte(jsonData), &d.Foos); err != nil {
panic(err)
}
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
// Use template.New("index").ParseFiles("index.html") for files
templ, err := template.New("index").Parse(htmlFile)
if err != nil {
panic(err)
}
if err := templ.Execute(rw, d); err != nil {
panic(err)
}
})
log.Panic(http.ListenAndServe(":3030", nil))
}

Golang json unmarshal according to key value pair

I have json as following
"data": [
{
"id": "recent_search",
"items": [],
"name": ""
},
{
"id": "popular_search",
"items": [],
"name": ""
},
{
"id": "digital",
"items": [],
"name": "DIGITAL"
}
]
and the structs are as follows:
type universeTypeData struct {
Recent universeSearchInfo
Popular universeSearchInfo
Digital universeSearchInfo
}
type universeSearchInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Items []universeSearchItem `json:"items"`
}
I want to unmarshal my json as "id" with value "recent_search" map to Recent, "id" with value "popular_search" map to Popular. Is there any way of doing this in golang?
My approach of doing it is
for _, v := range result.Data {
if v.ID == "in_category" {
finalResult.Universe.InCategory.ID = v.ID
finalResult.Universe.InCategory.Name = v.Name
for _, abc := range v.Items {
finalResult.Universe.InCategory.Items = append(finalResult.Universe.InCategory.Items, abc)
}
}
if v.ID == "recent_search" {
finalResult.Universe.Recent.ID = v.ID
finalResult.Universe.Recent.Name = v.Name
for _, abc := range v.Items {
finalResult.Universe.Recent.Items = append(finalResult.Universe.Recent.Items, abc)
}
}
if v.ID == "popular_search" {
finalResult.Universe.Popular.ID = v.ID
finalResult.Universe.Popular.Name = v.Name
for _, abc := range v.Items {
finalResult.Universe.Popular.Items = append(finalResult.Universe.Popular.Items, abc)
}
}
Is there any better way of doing it?
You want to unmurshal JSON array into Go struct which is not natural mapping. Any way, you most likely should be first unmurshal in slice and then parse this slice. Some workaround is to use json.Decoder
dec := json.NewDecoder(JSONdataReader)
var res universeTypeData
// read open bracket
dec.Token()
// while the array contains values
for dec.More() {
var m universeSearchInfo
// decode an array value
dec.Decode(&m)
switch m.ID {
case "recent_search":
res.Recent = m
case "popular_search":
res.Popular = m
case "digital":
res.Digital = m
}
}
// read closing bracket
dec.Token()
which allow you to decode on the fly, in one pass, without consuming intermediate slice representation. Working example
Implement Unmarshaler interface:
Unmarshaler is the interface implemented by types that can unmarshal a
JSON description of themselves. The input can be assumed to be a valid
encoding of a JSON value. UnmarshalJSON must copy the JSON data if it
wishes to retain the data after returning.
json unmarshaler interface assign the value from json to struct after parsing the result and applying conditions to fetch the value.
package main
import (
"encoding/json"
"fmt"
)
type Details struct {
Data []universeSearchInfo `json:"data"`
}
type universeTypeData struct {
Recent universeSearchInfo
Popular universeSearchInfo
Digital universeSearchInfo
}
type universeSearchInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Items []string `json:"items"`
}
func main() {
var result universeTypeData
jsonBytes := []byte(`{"data": [
{
"id": "recent_search",
"items": [],
"name": ""
},
{
"id": "popular_search",
"items": [],
"name": ""
},
{
"id": "digital",
"items": [],
"name": "DIGITAL"
}
]}`)
if err := json.Unmarshal(jsonBytes, &result); err != nil {
fmt.Println(err)
}
fmt.Println(result)
}
func (universeData *universeTypeData) UnmarshalJSON(data []byte) error {
var result Details
if err := json.Unmarshal(data, &result); err != nil {
return err
}
for _,value := range result.Data{
switch value.ID {
case "recent_search":
universeData.Recent = value
}
}
return nil
}
Working code on Go Playground

Unmarshalling json arrays as json objects

I have to unmarshal a series of Json objects, but one of the objects contain a json array which is not really structured in a good way.
"labels": [
{
"key": "owner",
"value": "harry"
},
{
"key": "group",
"value": "student"
}
]
I am unmarshalling it using this struct -
type StudentDetails struct {
Id string `json:"id"`
Name string `json:"name"`
Labels []Label `json:"labels,omitempty"`
}
type Label struct {
Key string `json:"key"`
Value string `json:"value"`
}
And I have to access it using x.Labels[0].key == "owner" inside a for loop which is very annoying.
I want to be able to do x.Labels.Owner == "harry" instead. How do I go about achieving this? The rest of JSON is unmarshalled fine using the default unmarshal function, so I don't think writing custom function will be good option.
With the constraints you have here, this is about as close as you will get (run in playground):
package main
import (
"encoding/json"
"fmt"
)
func main() {
j := `
{
"id": "42",
"name": "Marvin",
"labels": [
{
"key": "owner",
"value": "harry"
},
{
"key": "group",
"value": "student"
}
]
}`
d := StudentDetails{}
err := json.Unmarshal([]byte(j), &d)
if err != nil {
panic(err)
}
fmt.Println(d.Labels["owner"])
fmt.Println(d.Labels["group"])
}
type StudentDetails struct {
Id string `json:"id"`
Name string `json:"name"`
Labels Labels `json:"labels"`
}
type Labels map[string]string
func (l *Labels) UnmarshalJSON(b []byte) error {
a := []map[string]string{}
err := json.Unmarshal(b, &a)
if err != nil {
return err
}
t := map[string]string{}
for _, m := range a {
t[m["key"]] = m["value"]
}
*l = t
return nil
}
How about to define custom []Label type and add function on it.
For instance
type Labels []Label
func (l Labels) Owner() string {
if len(l) > 1 {
return l[0].Value
}
return ""
}

How to unmarshal JSON in to array of interface and use

I am having difficulty understanding how to correctly unmarshal some JSON data that goes in to an array of type inteface and then use it. I tried to make this example code as simple as possible to illustrate the problem I am having. The code can be found in the playground here: https://play.golang.org/p/U85J_lBJ7Zr
The output looks like:
[map[ObjectType:chair ID:1234 Brand:Blue Inc.] map[ID:5678
Location:Kitchen ObjectType:table]] { } false { } false
Code
package main
import (
"fmt"
"encoding/json"
)
type Chair struct {
ObjectType string
ID string
Brand string
}
type Table struct {
ObjectType string
ID string
Location string
}
type House struct {
Name string
Objects []interface{}
}
func main() {
var h House
data := returnJSONBlob()
err := json.Unmarshal(data, &h)
if err != nil {
fmt.Println(err)
}
fmt.Println(h.Objects)
s1, ok := h.Objects[0].(Table)
fmt.Println(s1, ok)
s2, ok := h.Objects[0].(Chair)
fmt.Println(s2, ok)
}
func returnJSONBlob() []byte {
s := []byte(`
{
"Name": "house1",
"Objects": [
{
"ObjectType": "chair",
"ID": "1234",
"Brand": "Blue Inc."
},
{
"ObjectType": "table",
"ID": "5678",
"Location": "Kitchen"
}
]
}
`)
return s
}
I'm not sure if this is practical, since this is a simplified version of your scenario. However, one way to do this is combine the two object types to a new one, Object, and then unmarshal them directly to Object instead of using interface{}:
package main
import (
"encoding/json"
"fmt"
)
type Object struct {
ObjectType string
ID string
Brand string
Location string
}
type House struct {
Name string
Objects []Object
}
func returnJSONBlob() []byte {
s := []byte(`
{
"Name": "house1",
"Objects": [
{
"ObjectType": "chair",
"ID": "1234",
"Brand": "Blue Inc."
},
{
"ObjectType": "table",
"ID": "5678",
"Location": "Kitchen"
}
]
}
`)
return s
}
func main() {
var h House
data := returnJSONBlob()
err := json.Unmarshal(data, &h)
if err != nil {
fmt.Println(err)
}
fmt.Println(h.Objects[0].Brand)
fmt.Println(h.Objects[1].Location)
}
Prints:
Blue Inc.
Kitchen
Example here: https://play.golang.org/p/91F4UrQlSjJ