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))
}
Related
I need to decode a JSON string with the float number like:
{"name":"Galaxy Nexus", "price":"3460.00"}
I use the Golang code below:
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string
Price float64
}
func main() {
s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
var pro Product
err := json.Unmarshal([]byte(s), &pro)
if err == nil {
fmt.Printf("%+v\n", pro)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", pro)
}
}
When I run it, get the result:
json: cannot unmarshal string into Go value of type float64
{Name:Galaxy Nexus Price:0}
I want to know how to decode the JSON string with type convert.
The answer is considerably less complicated. Just add tell the JSON interpeter it's a string encoded float64 with ,string (note that I only changed the Price definition):
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string
Price float64 `json:",string"`
}
func main() {
s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
var pro Product
err := json.Unmarshal([]byte(s), &pro)
if err == nil {
fmt.Printf("%+v\n", pro)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", pro)
}
}
Just letting you know that you can do this without Unmarshal and use json.decode. Here is Go Playground
package main
import (
"encoding/json"
"fmt"
"strings"
)
type Product struct {
Name string `json:"name"`
Price float64 `json:"price,string"`
}
func main() {
s := `{"name":"Galaxy Nexus","price":"3460.00"}`
var pro Product
err := json.NewDecoder(strings.NewReader(s)).Decode(&pro)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(pro)
}
Avoid converting a string to []byte: b := []byte(s). It allocates a new memory space and copy the whole the content into it.
strings.NewReader interface is better. Below is the code from godoc:
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
func main() {
const jsonStream = `
{"Name": "Ed", "Text": "Knock knock."}
{"Name": "Sam", "Text": "Who's there?"}
{"Name": "Ed", "Text": "Go fmt."}
{"Name": "Sam", "Text": "Go fmt who?"}
{"Name": "Ed", "Text": "Go fmt yourself!"}
`
type Message struct {
Name, Text string
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var m Message
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", m.Name, m.Text)
}
}
Passing a value in quotation marks make that look like string. Change "price":"3460.00" to "price":3460.00 and everything works fine.
If you can't drop the quotations marks you have to parse it by yourself, using strconv.ParseFloat:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type Product struct {
Name string
Price string
PriceFloat float64
}
func main() {
s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
var pro Product
err := json.Unmarshal([]byte(s), &pro)
if err == nil {
pro.PriceFloat, err = strconv.ParseFloat(pro.Price, 64)
if err != nil { fmt.Println(err) }
fmt.Printf("%+v\n", pro)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", pro)
}
}
I am working on a website scraper. I can send only 1 JSON data to JSON file regularly. I want to write one after another JSON data, so I need to keep hundreds of data in a single JSON file. like this
[
{
"id": 1321931,
"name": "Mike"
},
{
"id": 32139219,
"name": "Melissa"
},
{
"id": 8421921,
"name": "Jordan"
},
{
"id": 4291901,
"name": "David"
}
]
but output like this. When I send new data, just the first JSON data update itself.
[
{
"id": 1,
"name": "Mike"
}
]
here is the code:
package main
import (
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"strings"
"github.com/gocolly/colly"
)
type Info struct {
ID int `json:"id"`
Name string `json:"name"`
}
var tpl *template.Template
var name string
var stonf Info
var allInfos []Info
var id int
var co = colly.NewCollector()
func main() {
fmt.Println("Started...")
allInfos = make([]Info, 1)
id = rand.Intn((99999 - 10000) + 10000)
// Reading Data From Json
data, err := ioutil.ReadFile("stocky.json")
if err != nil {
fmt.Println("ERROR 1 JSON", err)
}
// Unmarshal JSON data
var d []Info
err = json.Unmarshal([]byte(data), &d)
if err != nil {
fmt.Println(err)
}
tpl, _ = tpl.ParseGlob("templates/*.html")
http.HandleFunc("/mete", hellloHandleFunc)
staticHandler := http.FileServer(http.Dir("./css/"))
http.Handle("/css/", http.StripPrefix("/css", staticHandler))
http.ListenAndServe("localhost:8080", nil)
}
func hellloHandleFunc(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Fatal(err)
}
allInfos[0].ID = id // JSON-PRO
// GET Price - Fiyat GETİR
co.OnHTML("div#dp", func(p *colly.HTMLElement) {
name = p.ChildText("h1#title")
})
requestLink := strings.TrimSpace(r.FormValue("input-link"))
co.Visit(requestLink)
// FIRST DATA JSON
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(allInfos)
stonf = Info{
Name: name,
}
fmt.Println("Index Running")
tpl.ExecuteTemplate(w, "form-copy.html", stonf)
}
func writeJson(data []Info) {
dataFile, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Println("Could not create JSON", err)
}
ioutil.WriteFile("stocky.json", dataFile, 0666)
}
Here is a solution which appends new Info to the list and store in file.
The solution will perform properly only for relatively small list. For large lists, the overhead of writing the entire file each time may be too high. In such case i propose to change the format to ndjson. It will allow to write only the current Info struct instead of the whole list.
I've also added synchronization mechanism to avoid race conditions in case you send multiple HTTP requests at the same time.
I assumed that the identifier must be generated separately for each request, and it is not a problem if collision occur.
package main
import (
"encoding/json"
"fmt"
"html/template"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"strings"
"sync"
"github.com/gocolly/colly"
)
type (
Info struct {
ID int `json:"id"`
Name string `json:"name"`
}
Infos struct {
List []Info
sync.Mutex
}
)
var (
infos *Infos
tpl *template.Template
co = colly.NewCollector()
)
func main() {
fmt.Println("Started...")
var err error
infos, err = readInfos()
if err != nil {
log.Fatal(err)
}
tpl, _ = tpl.ParseGlob("templates/*.html")
http.HandleFunc("/mete", hellloHandleFunc)
staticHandler := http.FileServer(http.Dir("./css/"))
http.Handle("/css/", http.StripPrefix("/css", staticHandler))
if err := http.ListenAndServe("localhost:8080", nil); err != nil {
log.Fatal(err)
}
}
func hellloHandleFunc(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Fatal(err)
}
stonf := Info{
ID: rand.Intn((99999 - 10000) + 10000),
}
// GET Price - Fiyat GETİR
co.OnHTML("div#dp", func(p *colly.HTMLElement) {
stonf.Name = p.ChildText("h1#title")
})
requestLink := strings.TrimSpace(r.FormValue("input-link"))
if err := co.Visit(requestLink); err != nil {
log.Fatal(err)
}
if err := infos.AppendAndWrite(stonf); err != nil {
log.Fatal(err)
}
// FIRST DATA JSON
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(stonf)
fmt.Println("Index Running")
tpl.ExecuteTemplate(w, "form-copy.html", stonf)
}
func readInfos() (*Infos, error) {
// Reading Data From Json
data, err := ioutil.ReadFile("stocky.json")
if err != nil {
return nil, err
}
var r []Info
// Unmarshal JSON data
err = json.Unmarshal([]byte(data), &r)
if err != nil {
return nil, err
}
return &Infos{List: r}, nil
}
func (i *Infos) AppendAndWrite(info Info) error {
i.Lock()
defer i.Unlock()
i.List = append(i.List, info)
if err := i.storeLocked(); err != nil {
return fmt.Errorf("storing info list failed: %w", err)
}
return nil
}
func (i *Infos) storeLocked() error {
dataFile, err := json.MarshalIndent(i.List, "", " ")
if err != nil {
return fmt.Errorf("could not marshal infos JSON: %w", err)
}
err = ioutil.WriteFile("stocky.json", dataFile, 0666)
if err != nil {
return fmt.Errorf("could not write 'stocky.json' file: %w", err)
}
return nil
}
There is a standard called JSON lines (https://jsonlines.org/) consisting on only one JSON per line instead of wrapping all in a JSON array.
JSON library from Go stdlib works pretty well with JSON lines on both cases, reading and writing.
Write multiple JSON (one per line):
e := json.NewEncoder(yourWriterFile)
e.Encode(object1)
e.Encode(object2)
//...
Read multiple JSON (one per line or concatenated):
d := json.NewDecoder(yourReaderFile)
d.Decode(&object1)
d.Decode(&object2)
//...
More info: https://pkg.go.dev/encoding/json
I'm new in Go. I'm trying to read a JSON file and get a part of it for then operate with the values obtained.
My JSON is in the file example.json:
{"results":[{"statement_id":0,"series":[{"name":"cpu/node_utilization","columns":["time","distinct"],"values":[[10,1],[11,3],[13,5]]}]}]}
So what I would like to get is the "values" for get the sum of all the elements. In this case: 1+3+5
Here is the code that I have. I'm available to get the results, but then I don't manage to get series.
Here is the code that I have:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
func main() {
// Open our jsonFile
jsonFile, err := os.Open("example.json")
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened example.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var all_data map[string]interface{}
json.Unmarshal([]byte(byteValue), &all_data)
fmt.Println(all_data["results"])
}
I've tried diferent solutions like
all_data["results"].(map[string]interface{})["series"])
But the problem is that the map is in an array, and I don't know how to solve it.
Using interfaces and map
package main
import (
"encoding/json"
"fmt"
)
func main() {
byteValue := []byte(`{"results":[{"statement_id":0,"series":[{"name":"cpu/node_utilization","columns":["time","distinct"],"values":[[10,1],[11,3],[13,5]]}]}]}`)
var all_data map[string][]interface{}
json.Unmarshal([]byte(byteValue), &all_data)
fmt.Println("result:", all_data["results"])
for _, r := range all_data["results"] {
s := r.(map[string]interface{})
fmt.Println("series:", s["series"])
w := s["series"].([]interface{})
for _, x := range w {
y := x.(map[string]interface{})
fmt.Println(y)
z := y["values"].([]interface{})
fmt.Println("values:", z)
for _, v := range z {
u := v.([]interface{})
fmt.Println(u)
for _, i := range u {
val := i.(float64)
fmt.Println(val)
}
}
}
}
}
I have solved defining an Struct.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type AutoGenerated struct {
Results []struct {
StatementID int `json:"statement_id"`
Series []struct {
Name string `json:"name"`
Columns []string `json:"columns"`
Values [][]int `json:"values"`
} `json:"series"`
} `json:"results"`
}
func main() {
// Open our jsonFile
jsonFile, err := os.Open("example.json")
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened example.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
all_data := AutoGenerated{}
json.Unmarshal([]byte(byteValue), &all_data)
fmt.Println(all_data.Results[0].Series[0].Values)
}
I have used this web to generate automatically the Struct providing the JSON structure
How can I read a json file into a struct, and then Marshal it back out to a json string with the Struct fields as keys (rather than the original json keys)?
(see Desired Output to Json File below...)
Code:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Rankings struct {
Keyword string `json:"keyword"`
GetCount uint32 `json:"get_count"`
Engine string `json:"engine"`
Locale string `json:"locale"`
Mobile bool `json:"mobile"`
}
func main() {
var jsonBlob = []byte(`
{"keyword":"hipaa compliance form", "get_count":157, "engine":"google", "locale":"en-us", "mobile":false}
`)
rankings := Rankings{}
err := json.Unmarshal(jsonBlob, &rankings)
if err != nil {
// nozzle.printError("opening config file", err.Error())
}
rankingsJson, _ := json.Marshal(rankings)
err = ioutil.WriteFile("output.json", rankingsJson, 0644)
fmt.Printf("%+v", rankings)
}
Output on screen:
{Keyword:hipaa compliance form GetCount:157 Engine:google Locale:en-us Mobile:false}
Output to Json File:
{"keyword":"hipaa compliance form","get_count":157,"engine":"google","locale":"en-us","mobile":false}
Desired Output to Json File:
{"Keyword":"hipaa compliance form","GetCount":157,"Engine":"google","Locale":"en-us","Mobile":false}
If I understand your question correctly, all you want to do is remove the json tags from your struct definition.
So:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Rankings struct {
Keyword string
GetCount uint32
Engine string
Locale string
Mobile bool
}
func main() {
var jsonBlob = []byte(`
{"keyword":"hipaa compliance form", "get_count":157, "engine":"google", "locale":"en-us", "mobile":false}
`)
rankings := Rankings{}
err := json.Unmarshal(jsonBlob, &rankings)
if err != nil {
// nozzle.printError("opening config file", err.Error())
}
rankingsJson, _ := json.Marshal(rankings)
err = ioutil.WriteFile("output.json", rankingsJson, 0644)
fmt.Printf("%+v", rankings)
}
Results in:
{Keyword:hipaa compliance form GetCount:0 Engine:google Locale:en-us Mobile:false}
And the file output is:
{"Keyword":"hipaa compliance form","GetCount":0,"Engine":"google","Locale":" en-us","Mobile":false}
Running example at http://play.golang.org/p/dC3s37HxvZ
Note: GetCount shows 0, since it was read in as "get_count". If you want to read in JSON that has "get_count" vs. "GetCount", but output "GetCount" then you'll have to do some additional parsing.
See Go- Copy all common fields between structs for additional info about this particular situation.
Try to change the json format in the struct
type Rankings struct {
Keyword string `json:"Keyword"`
GetCount uint32 `json:"Get_count"`
Engine string `json:"Engine"`
Locale string `json:"Locale"`
Mobile bool `json:"Mobile"`
}
An accourance happened by just using json.Marshal() / json.MarshalIndent().
It overwrites the existing file, which in my case was suboptimal. I just wanted to add content to current file, and keep old content.
This writes data through a buffer, with bytes.Buffer type.
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, together with the standard json.Marshal() or json.MarshalIndent() which overwrites the file
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)
}
Why is this useful?
File.Write() returns bytes written to the file! So this is perfect if you want to manage memory or storage.
WriteDataToFileAsJSON() (numberOfBytesWritten, error)
I need to decode a JSON string with the float number like:
{"name":"Galaxy Nexus", "price":"3460.00"}
I use the Golang code below:
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string
Price float64
}
func main() {
s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
var pro Product
err := json.Unmarshal([]byte(s), &pro)
if err == nil {
fmt.Printf("%+v\n", pro)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", pro)
}
}
When I run it, get the result:
json: cannot unmarshal string into Go value of type float64
{Name:Galaxy Nexus Price:0}
I want to know how to decode the JSON string with type convert.
The answer is considerably less complicated. Just add tell the JSON interpeter it's a string encoded float64 with ,string (note that I only changed the Price definition):
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string
Price float64 `json:",string"`
}
func main() {
s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
var pro Product
err := json.Unmarshal([]byte(s), &pro)
if err == nil {
fmt.Printf("%+v\n", pro)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", pro)
}
}
Just letting you know that you can do this without Unmarshal and use json.decode. Here is Go Playground
package main
import (
"encoding/json"
"fmt"
"strings"
)
type Product struct {
Name string `json:"name"`
Price float64 `json:"price,string"`
}
func main() {
s := `{"name":"Galaxy Nexus","price":"3460.00"}`
var pro Product
err := json.NewDecoder(strings.NewReader(s)).Decode(&pro)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(pro)
}
Avoid converting a string to []byte: b := []byte(s). It allocates a new memory space and copy the whole the content into it.
strings.NewReader interface is better. Below is the code from godoc:
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
func main() {
const jsonStream = `
{"Name": "Ed", "Text": "Knock knock."}
{"Name": "Sam", "Text": "Who's there?"}
{"Name": "Ed", "Text": "Go fmt."}
{"Name": "Sam", "Text": "Go fmt who?"}
{"Name": "Ed", "Text": "Go fmt yourself!"}
`
type Message struct {
Name, Text string
}
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var m Message
if err := dec.Decode(&m); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: %s\n", m.Name, m.Text)
}
}
Passing a value in quotation marks make that look like string. Change "price":"3460.00" to "price":3460.00 and everything works fine.
If you can't drop the quotations marks you have to parse it by yourself, using strconv.ParseFloat:
package main
import (
"encoding/json"
"fmt"
"strconv"
)
type Product struct {
Name string
Price string
PriceFloat float64
}
func main() {
s := `{"name":"Galaxy Nexus", "price":"3460.00"}`
var pro Product
err := json.Unmarshal([]byte(s), &pro)
if err == nil {
pro.PriceFloat, err = strconv.ParseFloat(pro.Price, 64)
if err != nil { fmt.Println(err) }
fmt.Printf("%+v\n", pro)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", pro)
}
}