Golang: how to parse json that get trait and data both? - json

I got a dic data with {{"word name":"word meaning"},{"word name":"word meaning"},…}
I want to parse to map of the words.
I tried to write code with interface{} but i cant imagine how to. Thanks for reading.

If you have a way of changing first and last curly brace, to a square brackets, then you could do the following:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
var raw_list []map[string]string
var jsonText = []byte(`[
{"Cat": "small animal"},
{"Cow": "Big animal"}
]`)
err := json.Unmarshal(jsonText, &raw_list)
if err != nil {
log.Fatal(err)
}
fmt.Printf("raw_list : %s\n", raw_list)
new_map := make(map[string]string)
for _, ele := range raw_list {
for key, val := range ele {
new_map[key] = val
}
}
fmt.Printf("new_map %s\n", new_map)
}
GoPlayground
Result:
raw_list : [map[Cat:small animal] map[Cow:Big animal]]
new_map map[Cat:small animal Cow:Big animal]

For example:
package main
import (
"encoding/json"
"fmt"
)
var jsonText = []byte(`[
{"Name": "Cat", "Meaning": "A Cat"},
{"Name": "Dog", "Meaning": "A Dog"}
]`)
type Word struct {
Name, Meaning string
}
func main() {
var words []Word
dict := make(map[string]string)
if err := json.Unmarshal(jsonText, &words); err != nil {
fmt.Println("error:", err)
}
for _, w := range words {
dict[w.Name] = w.Meaning
}
fmt.Printf("%v", dict)
}
Produces:
map[Cat:A Cat Dog:A Dog]
Playground

Your JSON should be like the following
{
"word 1": "meaning 1",
"word 2": "meaning 2"
}
to accomplish that you may use something like that
func fixJson(s string) string {
s = strings.Replace(s, "{", "", -1)
s = strings.Replace(s, "}", "", -1)
return "{" + s + "}"
}
Full code
package main
import (
"encoding/json"
"strings"
"fmt"
"log"
)
func fixJson(s string) string {
s = strings.Replace(s, "{", "", -1)
s = strings.Replace(s, "}", "", -1)
return "{" + s + "}"
}
func main() {
var words map[string]string
var text = `{
{"word 1": "meaning 1"},
{"word 2": "meaning 2"}
}`
var jsonText = []byte(fixJson(text))
err := json.Unmarshal(jsonText, &words)
if err != nil {
log.Fatal(err)
}
fmt.Println(words)
}
Playground

Related

Is there a way to loop over a json file and get each struct into a separate file -- Golang?

So my program is to read through a csv file and convert it into json. i've been able to do that and write the json to a file however i want to be able to take each struct or object from this json file and write to a file.
i've attached my code for more understanding
package main
import (
"crypto/sha256"
"encoding/csv"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"strconv"
)
type NftRecord struct{
SeriesNumber int `json:"Series Number"`
Name string `json:"Name"`
//TheDescriptor string `json:"The Descriptor"`
//NewName string `json:"New Name"`
Hash string `json:"Hash"`
UUIDs string `json:"UUIDs"`
Description string `json:"Description"`
DriveLink string `json:"Drive Link"`
}
func createRecordsList(data [][]string) []NftRecord{
var recordList []NftRecord
for i, line := range data{
if i > 0{
var rec NftRecord
for j, field := range line{
if j == 0{
var err error
rec.SeriesNumber, err = strconv.Atoi(field)
if err != nil{
continue
}
}else if j == 4{
rec.Name = field
}else if j == 7 {
rec.Description = field
}else if j ==6{
rec.UUIDs = field
}else if j == 5{
rec.Hash = field
}else if j == 8{
rec.DriveLink = field
} else if j == 13{
var err error
if err != nil{
continue
}
}
}
recordList = append(recordList, rec)
}
}
return recordList
}
func main(){
f, err := os.Open("./scaler.csv")
if err != nil {
log.Fatal(err)
}
defer f.Close()
csvReader := csv.NewReader(f)
data, err := csvReader.ReadAll()
if err != nil {
log.Fatal(err)
}
recordList := createRecordsList(data)
jsonData, err := json.MarshalIndent(recordList, "", " ")
if err != nil {
log.Fatal(err)
}
_ = ioutil.WriteFile("./test.json", jsonData, 0644)
fmt.Println(string(jsonData))
//hashing
hash := sha256.New()
if _, err := io.Copy(hash, f); err != nil {
panic(err)
}
sum := fmt.Sprintf("%x", hash.Sum(nil))
fmt.Println(sum)
}
i've tried pretty much everything i guess, i'm hoping i could get past this as i've got other features to add.
This is the JSON below
{
"Series Number": 301,
"Name": "wendy-the -banker",
"Hash": "D615D6E161E5F820A2B9F5C3ED3867BE0AA9F16DDCA00A0550E30E29A0B467AD",
"UUIDs": "587645e2-5ace-11ed-9b6a-0242ac120002",
"Description": "Wendy loves working in the bank, as she gets to meet different people.",
"Drive Link": "Link"
},
{
"Series Number": 302,
"Name": "uduak-the -dark horse",
"Hash": "5A63A94F455D367EC0AA39F57F827DDBECD3F335C817ED5A76460A2AB8E9068A",
"UUIDs": "58764862-5ace-11ed-9b6a-0242ac120002",
"Description": "Uduak looks innocent and has the whole apartment bugged",
"Drive Link": "Link"
},
Loop over recordList. For each element marshall it to JSON and write it to a unique file. You’ll have to have a unique file naming scheme but a trivial one to implement would be a counter (eg 1.json, 2.json, etc).

No Values when Loading Json from File Golang

I hope someone could help me with this issue because I have been scratching my head for a while.
I have a project where I am trying to load json into a struct in go. I have followed exactly several tutorials online, but keep getting no data back and no error.
My json file is called page_data.json and looks like:
[
{
"page_title": "Page1",
"page_description": "Introduction",
"link": "example_link",
"authors":
[
"Author1",
"Author2",
"Author3",
]
},
// second object, same as the first
]
But when I try the following in go:
package main
import (
"fmt"
"encoding/json"
"os"
"io/ioutil"
)
type PageData struct {
Title string `json: "page_title"`
Description string `json: "page_description"`
Link string `json: "link"`
Authors []string `json: "authors"`
}
func main() {
var numPages int = LoadPageData("page_data.json")
fmt.Printf("Num Pages: %d", numPages)
}
func LoadPageData(path string) int {
jsonFile, err := os.Open(path)
if err != nil {
fmt.Println(err)
}
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var pageList []PageData
json.Unmarshal(byteValue, &pageList)
return len(pageList)
}
the output I get is:
Num Pages: 0
Fix the JSON commas and the Go struct field tags. For example,
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type PageData struct {
Title string `json:"page_title"`
Description string `json:"page_description"`
Link string `json:"link"`
Authors []string `json:"authors"`
}
func main() {
var numPages int = LoadPageData("page_data.json")
fmt.Printf("Num Pages: %d\n", numPages)
}
func LoadPageData(path string) int {
jsonFile, err := os.Open(path)
if err != nil {
fmt.Println(err)
}
defer jsonFile.Close()
byteValue, err := ioutil.ReadAll(jsonFile)
if err != nil {
fmt.Println(err)
}
var pageList []PageData
err = json.Unmarshal(byteValue, &pageList)
if err != nil {
fmt.Println(err)
}
fmt.Println(pageList)
return len(pageList)
}
Output:
[{Page1 Introduction example_link [Author1 Author2 Author3]}]
page_data.json:
[
{
"page_title": "Page1",
"page_description": "Introduction",
"link": "example_link",
"authors":
[
"Author1",
"Author2",
"Author3"
]
}
]

Converting map to string in Go

I am trying to find the best way to convert map[string]string to type string.
I tried converting to JSON with marshalling to keep the format and then converting back to a string, but this was not successful.
More specifically, I am trying to convert a map containing keys and values to a string to accommodate Environment Variables and structs.go.
For example, the final string should be like
LOG_LEVEL="x"
API_KEY="y"
The map
m := map[string]string{
"LOG_LEVEL": "x",
"API_KEY": "y",
}
You need some key=value pair on each line representing one map entry, and you need quotes around the values:
package main
import (
"bytes"
"fmt"
)
func createKeyValuePairs(m map[string]string) string {
b := new(bytes.Buffer)
for key, value := range m {
fmt.Fprintf(b, "%s=\"%s\"\n", key, value)
}
return b.String()
}
func main() {
m := map[string]string{
"LOG_LEVEL": "DEBUG",
"API_KEY": "12345678-1234-1234-1234-1234-123456789abc",
}
println(createKeyValuePairs(m))
}
Here is a working example on Go Playground.
You can use fmt.Sprint to convert the map to string:
import (
"fmt"
)
func main() {
m := map[string]string{
"a": "b",
"c": "d",
}
log.Println("Map: " + fmt.Sprint(m))
}
Or fmt.Sprintf:
import (
"fmt"
)
func main() {
m := map[string]string{
"a": "b",
"c": "d",
}
log.Println(fmt.Sprintf("Map: %v", m))
}
I would do this very simple and pragmatic:
package main
import (
"fmt"
)
func main() {
m := map[string]string {
"LOG_LEVEL": "x",
"API_KEY": "y",
}
var s string
for key, val := range m {
// Convert each key/value pair in m to a string
s = fmt.Sprintf("%s=\"%s\"", key, val)
// Do whatever you want to do with the string;
// in this example I just print out each of them.
fmt.Println(s)
}
}
You can see this in action in The Go Playground.
This could work:
// Marshal the map into a JSON string.
mJson, err := json.Marshal(m)
if err != nil {
fmt.Println(err.Error())
return
}
jsonStr := string(mJson)
fmt.Println("The JSON data is: ")
fmt.Println(jsonStr)
We could convert map to single line using sf.MapToStr function from github.com/wissance/stringFormatter v1.0.1 (https://github.com/Wissance/stringFormatter):
// ...
import (
sf "github.com/wissance/stringFormatter"
)
// ...
func MyFunc() {
options := map[string]interface{}{
"connectTimeout": 1000,
"useSsl": true,
"login": "sa",
"password": "sa",
}
str := sf.MapToString(&options, sf.KeyValueWithSemicolonSepFormat, ", ")
fmt.Println(str)
}
jsonString, err := json.Marshal(datas)
fmt.Println(err)

create a map of string from a JSON with unknow key-values in Go

I try to create a map of strings from a JSON with an undefined number of unknow key-values.
Here is my example JSON file:
{
"localhost":
{
"tag": "dev_latest",
"vhost": "localhost.com"
},
"development":
{
"tag": "dev_latest",
"vhost": "dev.com"
}
}
I want to create a map[string]string with value like this:
config := map[string]string{
"localhost-tag": "dev_latest",
"localhost-vhost": "localhost.com,
"development-tag": "dev_latest,
...
}
To parse a JSON with "github.com/jmoiron/jsonq" with known values, is quite easy, but in this case, localhost can be anything and tag can be any other thing.
My entry point in my Go code is like this:
func ParseJson(){
configPath := GetConfigPath()
b, err := ioutil.ReadFile(configPath)
// Here, I need to create my map of strings..
return configKeyStr
}
Any help will be really appreciate.
Thanks!
Easy to do. Simply convert.
package main
import (
"encoding/json"
"fmt"
"log"
)
const s = `
{
"localhost":
{
"tag": "dev_latest",
"vhost": "localhost.com"
},
"development":
{
"tag": "dev_latest",
"vhost": "dev.com"
}
}
`
func main() {
var m map[string]interface{}
err := json.Unmarshal([]byte(s), &m)
if err != nil {
log.Fatal(err)
}
mm := make(map[string]string)
for k, v := range m {
mm[k] = fmt.Sprint(v)
}
fmt.Println(mm)
}
UPDATE
Wrote flatten (maybe works as charm)
package main
import (
"encoding/json"
"fmt"
"log"
"reflect"
)
const s = `
{
"localhost":
{
"tag": "dev_latest",
"vhost": "localhost.com"
},
"development":
{
"tag": "dev_latest",
"vhost": "dev.com"
}
}
`
func flatten(m map[string]interface{}) map[string]string {
mm := make(map[string]string)
for k, v := range m {
switch reflect.TypeOf(v).Kind() {
case reflect.Map:
mv := flatten(v.(map[string]interface{}))
for kk, vv := range mv {
mm[k+"-"+kk] = vv
}
case reflect.Array, reflect.Slice:
for kk, vv := range m {
if reflect.TypeOf(vv).Kind() == reflect.Map {
mv := flatten(vv.(map[string]interface{}))
for kkk, vvv := range mv {
mm[k+"-"+kkk] = vvv
}
} else {
mm[k+"-"+kk] = fmt.Sprint(vv)
}
}
default:
mm[k] = fmt.Sprint(v)
}
}
return mm
}
func main() {
var m map[string]interface{}
err := json.Unmarshal([]byte(s), &m)
if err != nil {
log.Fatal(err)
}
b, _ := json.MarshalIndent(flatten(m), "", " ")
println(string(b))
}
You can't have this automatically, but you can range over the "internal" maps, and combine the outer keys with the inner keys using simple string concatenation (+ operator). Also it's recommended to unmarshal directly into a value of map[string]map[string]string so you don't need to use type assertions. Also no need to use any external libraries for this, the standard encoding/json package is perfectly enough for this.
Example:
var mm map[string]map[string]string
if err := json.Unmarshal([]byte(src), &mm); err != nil {
panic(err)
}
config := map[string]string{}
for mk, m := range mm {
for k, v := range m {
config[mk+"-"+k] = v
}
}
fmt.Println(config)
Output is as expected (try it on the Go Playground):
map[localhost-tag:dev_latest localhost-vhost:localhost.com
development-tag:dev_latest development-vhost:dev.com]
Since in the question you mentioned undefined number of unknown key-values, you may need to deal with JSON document with unknown number of nesting level and having a value other than string. In this case, you need to Unmarshal json to map[string]interface{}, then use recursion to make flat map. Once the json document unmrashaled to map[string]interface{}, use the following function:
func flatMap(src map[string]interface{}, baseKey, sep string, dest map[string]string) {
for key, val := range src {
if len(baseKey) != 0 {
key = baseKey + sep + key
}
switch val := val.(type) {
case map[string]interface{}:
flatMap(val, key, sep, dest)
case string:
dest[key] = val
case fmt.Stringer:
dest[key] = val.String()
default:
//TODO: You may need to handle ARRAY/SLICE
//simply convert to string using `Sprintf`
//NOTE: modify as needed.
dest[key] = fmt.Sprintf("%v", val)
}
}
}
The working solution adapted from mattn answer at https://play.golang.org/p/9SQsbAUFdY
As pointed by mattn, you may have problem when you want to writeback the configuration value. In that case, use the existing library/framework.

How can I pretty-print JSON using Go?

Does anyone know of a simple way to pretty-print JSON output in Go?
I'd like to pretty-print the result of json.Marshal, as well as formatting an existing string of JSON so it's easier to read.
MarshalIndent will allow you to output your JSON with indentation and spacing. For example:
{
"data": 1234
}
The indent argument specifies the series of characters to indent with. Thus, json.MarshalIndent(data, "", " ") will pretty-print using four spaces for indentation.
The accepted answer is great if you have an object you want to turn into JSON. The question also mentions pretty-printing just any JSON string, and that's what I was trying to do. I just wanted to pretty-log some JSON from a POST request (specifically a CSP violation report).
To use MarshalIndent, you would have to Unmarshal that into an object. If you need that, go for it, but I didn't. If you just need to pretty-print a byte array, plain Indent is your friend.
Here's what I ended up with:
import (
"bytes"
"encoding/json"
"log"
"net/http"
)
func HandleCSPViolationRequest(w http.ResponseWriter, req *http.Request) {
body := App.MustReadBody(req, w)
if body == nil {
return
}
var prettyJSON bytes.Buffer
error := json.Indent(&prettyJSON, body, "", "\t")
if error != nil {
log.Println("JSON parse error: ", error)
App.BadRequest(w)
return
}
log.Println("CSP Violation:", string(prettyJSON.Bytes()))
}
For better memory usage, I guess this is better:
var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
if err := enc.Encode(data); err != nil {
panic(err)
}
I was frustrated by the lack of a fast, high quality way to marshal JSON to a colorized string in Go so I wrote my own Marshaller called ColorJSON.
With it, you can easily produce output like this using very little code:
package main
import (
"fmt"
"encoding/json"
"github.com/TylerBrock/colorjson"
)
func main() {
str := `{
"str": "foo",
"num": 100,
"bool": false,
"null": null,
"array": ["foo", "bar", "baz"],
"obj": { "a": 1, "b": 2 }
}`
var obj map[string]interface{}
json.Unmarshal([]byte(str), &obj)
// Make a custom formatter with indent set
f := colorjson.NewFormatter()
f.Indent = 4
// Marshall the Colorized JSON
s, _ := f.Marshal(obj)
fmt.Println(string(s))
}
I'm writing the documentation for it now but I was excited to share my solution.
Edit Looking back, this is non-idiomatic Go. Small helper functions like this add an extra step of complexity. In general, the Go philosophy prefers to include the 3 simple lines over 1 tricky line.
As #robyoder mentioned, json.Indent is the way to go. Thought I'd add this small prettyprint function:
package main
import (
"bytes"
"encoding/json"
"fmt"
)
//dont do this, see above edit
func prettyprint(b []byte) ([]byte, error) {
var out bytes.Buffer
err := json.Indent(&out, b, "", " ")
return out.Bytes(), err
}
func main() {
b := []byte(`{"hello": "123"}`)
b, _ = prettyprint(b)
fmt.Printf("%s", b)
}
https://go-sandbox.com/#/R4LWpkkHIN or http://play.golang.org/p/R4LWpkkHIN
Here's what I use. If it fails to pretty print the JSON it just returns the original string. Useful for printing HTTP responses that should contain JSON.
import (
"encoding/json"
"bytes"
)
func jsonPrettyPrint(in string) string {
var out bytes.Buffer
err := json.Indent(&out, []byte(in), "", "\t")
if err != nil {
return in
}
return out.String()
}
package cube
import (
"encoding/json"
"fmt"
"github.com/magiconair/properties/assert"
"k8s.io/api/rbac/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"testing"
)
func TestRole(t *testing.T) {
clusterRoleBind := &v1beta1.ClusterRoleBinding{
ObjectMeta: v1.ObjectMeta{
Name: "serviceaccounts-cluster-admin",
},
RoleRef: v1beta1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: "cluster-admin",
},
Subjects: []v1beta1.Subject{{
Kind: "Group",
APIGroup: "rbac.authorization.k8s.io",
Name: "system:serviceaccounts",
},
},
}
b, err := json.MarshalIndent(clusterRoleBind, "", " ")
assert.Equal(t, nil, err)
fmt.Println(string(b))
}
Here is my solution:
import (
"bytes"
"encoding/json"
)
const (
empty = ""
tab = "\t"
)
func PrettyJson(data interface{}) (string, error) {
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
encoder.SetIndent(empty, tab)
err := encoder.Encode(data)
if err != nil {
return empty, err
}
return buffer.String(), nil
}
//You can do it with json.MarshalIndent(data, "", " ")
package main
import(
"fmt"
"encoding/json" //Import package
)
//Create struct
type Users struct {
ID int
NAME string
}
//Asign struct
var user []Users
func main() {
//Append data to variable user
user = append(user, Users{1, "Saturn Rings"})
//Use json package the blank spaces are for the indent
data, _ := json.MarshalIndent(user, "", " ")
//Print json formatted
fmt.Println(string(data))
}
Another example with http.ResponseWriter.
import (
"encoding/json"
"net/http"
)
func main() {
var w http.ResponseWriter
type About struct {
ProgName string
Version string
}
goObj := About{ProgName: "demo", Version: "0.0.0"}
beautifulJsonByte, err := json.MarshalIndent(goObj, "", " ")
if err != nil {
panic(err)
}
_, _ = w.Write(beautifulJsonByte)
}
output
{
"ProgName": "demo",
"Version": "0.0.0"
}
If you want to create a commandline utility to pretty print JSON
package main
import ("fmt"
"encoding/json"
"os"
"bufio"
"bytes"
)
func main(){
var out bytes.Buffer
reader := bufio.NewReader(os.Stdin)
text, _ := reader.ReadString('\n')
err := json.Indent(&out, []byte(text), "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out.Bytes()))
}
echo "{\"boo\":\"moo\"}" | go run main.go
will produce the following output :
{
"boo": "moo"
}
feel free to build a binary
go build main.go
and drop it in /usr/local/bin
A simple off the shelf pretty printer in Go. One can compile it to a binary through:
go build -o jsonformat jsonformat.go
It reads from standard input, writes to standard output and allow to set indentation:
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"os"
)
func main() {
indent := flag.String("indent", " ", "indentation string/character for formatter")
flag.Parse()
src, err := ioutil.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "problem reading: %s", err)
os.Exit(1)
}
dst := &bytes.Buffer{}
if err := json.Indent(dst, src, "", *indent); err != nil {
fmt.Fprintf(os.Stderr, "problem formatting: %s", err)
os.Exit(1)
}
if _, err = dst.WriteTo(os.Stdout); err != nil {
fmt.Fprintf(os.Stderr, "problem writing: %s", err)
os.Exit(1)
}
}
It allows to run a bash commands like:
cat myfile | jsonformat | grep "key"
i am sort of new to go, but this is what i gathered up so far:
package srf
import (
"bytes"
"encoding/json"
"os"
)
func WriteDataToFileAsJSON(data interface{}, filedir string) (int, error) {
//write data as buffer to json encoder
buffer := new(bytes.Buffer)
encoder := json.NewEncoder(buffer)
encoder.SetIndent("", "\t")
err := encoder.Encode(data)
if err != nil {
return 0, err
}
file, err := os.OpenFile(filedir, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
return 0, err
}
n, err := file.Write(buffer.Bytes())
if err != nil {
return 0, err
}
return n, nil
}
This is the execution of the function, and just standard
b, _ := json.MarshalIndent(SomeType, "", "\t")
Code:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
minerals "./minerals"
srf "./srf"
)
func main() {
//array of Test struct
var SomeType [10]minerals.Test
//Create 10 units of some random data to write
for a := 0; a < 10; a++ {
SomeType[a] = minerals.Test{
Name: "Rand",
Id: 123,
A: "desc",
Num: 999,
Link: "somelink",
People: []string{"John Doe", "Aby Daby"},
}
}
//writes aditional data to existing file, or creates a new file
n, err := srf.WriteDataToFileAsJSON(SomeType, "test2.json")
if err != nil {
log.Fatal(err)
}
fmt.Println("srf printed ", n, " bytes to ", "test2.json")
//overrides previous file
b, _ := json.MarshalIndent(SomeType, "", "\t")
ioutil.WriteFile("test.json", b, 0644)
}
Use json.MarshalIndent with string
This easyPrint function accepts argument data (any type of data) to print it into the intended (pretty) JSON format.
import (
"encoding/json"
"log"
)
func easyPrint(data interface{}) {
manifestJson, _ := json.MarshalIndent(data, "", " ")
log.Println(string(manifestJson))
}
With name argument.
TODO: make argument name optional.
func easyPrint(data interface{}, name string) {
manifestJson, _ := json.MarshalIndent(data, "", " ")
log.Println(name + " ->", string(manifestJson))
}