Go template to struct - go

I have a Go template that should resolve to a struct. How can I convert the bytes.Bufferresult from template execute function back to the struct. Playground
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
"text/template"
)
type Data struct {
Age int
Username string
SubData SubData
}
type SubData struct {
Name string
}
func main() {
s := SubData{Name: "J. Jr"}
d := Data{Age: 26, Username: "HelloWorld", SubData: s}
tmpl := "{{ .SubData }}"
t := template.New("My template")
t, _ = t.Parse(string(tmpl))
buffer := new(bytes.Buffer)
t.Execute(buffer, d)
fmt.Println(buffer)
// writing
enc := gob.NewEncoder(buffer)
err := enc.Encode(s)
if err != nil {
log.Fatal("encode error:", err)
}
// reading
buffer = bytes.NewBuffer(buffer.Bytes())
e := new(SubData)
dec := gob.NewDecoder(buffer)
err = dec.Decode(e)
if err != nil {
log.Fatal("decode error:", err)
}
fmt.Println(e, err)
}

You cannot. This is plain simply impossible.
But why on earth would anybody want to do something like this? Why don't you just send your Data directly via gob and decode it directly? Why creating a textual representation which you gob?

Related

Custom marshalling of a slice of structs [duplicate]

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))
}

unicode character in result execute template

I have this template
<data><datos name={{.Name}} phone={{.Phone}} email={{.Email}}></data>
And I'm running it like this:
buf := &bytes.Buffer{}
t := template.Must(template.New("dataPdf").Parse("<data><datos name={{.Name}} phone={{.Phone}} email={{.email}}></data>"))
err := t.Execute(buf, data)
if err != nil {
panic(err)
}
return buf.String()
All values ​​are overridden, but for some reason the < and > characters remain in unicode
\u003cdatos\u003e\
I update:
Verify and the error doesn't occur until this string value is assigned to a specific Struct object and then I try to convert it to json.
I cannot reproduce this. See a working example on the Go Playground https://play.golang.org/p/xKmjgzZxeES.
The output of the code below is
<data><datos name=Joe phone=123-456-7890 email=hunter2#a.com></data>
package main
import (
"bytes"
"fmt"
"text/template"
)
type Person struct {
Name string
Phone string
Email string
}
func main() {
buf := &bytes.Buffer{}
t := template.Must(template.New("dataPdf").Parse("<data><datos name={{.Name}} phone={{.Phone}} email={{.Email}}></data>"))
data := Person{Name: "Joe", Phone: "123-456-7890", Email: "hunter2#a.com"}
err := t.Execute(buf, data)
if err != nil {
panic(err)
}
fmt.Printf(buf.String())
}

Decode/Encode multi-line string fields from YAML using golang

YAML files can contain fields with "multi-line string" data. Example below:
Data:
Foo: |
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
How can I properly encode and decode these in Golang and what should be the type of the Data field, map[string][]byte?
I tried:
import (
yamlv2 "gopkg.in/yaml.v2"
)
type data struct {
Data map[string][]byte
}
func decode(bytes []byte) (*data, error) {
d := &data{}
err := yamlv2.Unmarshal(bytes, d)
if err != nil {
return nil, err
}
return d, nil
}
Also see in Playground.
You would parse a multi-line string the same way as a normal string:
package main
import (
"fmt"
"gopkg.in/yaml.v2"
"log"
)
var yamlString = `
Data:
Foo: |
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten`
type data struct {
Data map[string]string `yaml:"Data"`
}
func main() {
t := data{}
err := yaml.Unmarshal([]byte(yamlString), &t)
if err != nil {
log.Fatalf("error: %v", err)
}
if foo, ok := t.Data["Foo"]; ok {
fmt.Printf("--- t:\n%v\n\n", foo)
}
}

Sending a CSV file Server to Client

I've just recently started trying to learn Go and I'm trying to write a small server/client application for sending a csv file, from a server to a client. I'm running into an invalid type error when trying to encode a struct into BigEndian binary. My struct seems to already be in a binary format, I'm unsure why I get the following error:
Error writing binary buffer to big endian binary.Write: invalid type *main.DataPack
Also, I'd like to keep the TCP connection open after sending the file, that's why I'm not using io.Copy.
Currently I'm triggering the handling of the file upload through by sending a '\x00' byte:
// Server
package main
import (
"path/filepath"
"fmt"
"io/ioutil"
"net"
"os"
"encoding/binary"
"bytes"
)
type DataPack struct {
Length int64
Contents []byte
}
func main() {
absPath, _ := filepath.Abs("./progs.csv")
data, _ := ioutil.ReadFile(absPath)
fmt.Println("%s",data)
tel, err := net.Listen("tcp", "0.0.0.0:23")
if err != nil {
fmt.Println(err)
return
}
for {
conn, err := tel.Accept()
if err != nil {
break
}
fmt.Println("above filehandler")
go fileHandler(conn)
}
}
func fileHandler(conn net.Conn) {
buf := make([]byte, 0)
buf = append(buf, '\x00')
conn.Write(buf)
absPath, _ := filepath.Abs("./progs.csv")
file, err := os.Open(absPath)
defer file.Close()
fi, err := file.Stat()
if err != nil {
fmt.Println("Fatal error reading file: ", err)
}
fmt.Println("This is the length of the file: ", fi.Size())
data := &DataPack{Length: fi.Size()} // , Contents: }
data.Contents = make([]byte, data.Length)
n, err := file.Read(data.Contents)
if err != nil {
fmt.Println("error reading contents into struct: ", err)
}
fmt.Println("tried to read file contents: ", n)
fmt.Println("DataPack: %+v", data)
buf1 := new(bytes.Buffer)
err = binary.Write(buf1, binary.BigEndian, &data)
if err != nil {
fmt.Println("Error writing binary buffer to big endian ", err)
}
conn.Write(buf1.Bytes())
}
Here is the client
package main
import (
"log"
"fmt"
"net"
"strings"
"strconv"
"bufio"
)
const (
host = "127.0.0.1"
port = 23
)
func main() {
addr := strings.Join([]string{host, strconv.Itoa(port)}, ":")
client := NewClient()
var err error
client.socket, err = net.Dial("tcp", addr)
if err != nil {
log.Println("error setting up socket: %s", err)
}
for {
m := bufio.NewReader(client.socket)
b, err := m.ReadByte()
if err != nil {
fmt.Println("here is the error: ", err)
}
if b == '\x00'{
fmt.Println("about to receive a file!!!")
b, _ := m.ReadByte()
fmt.Println("just got another byte ", b )
}
}
log.Printf("Over")
}
why you get the error
Visit https://golang.org/pkg/encoding/binary/#Write
Write writes the binary representation of data into w. Data must be a fixed-size value or a slice of fixed-size values, or a pointer to such data.
type DataPack struct {
Length int64
Contents []byte
}
Contents isn't a fixed-size value, so you got the invalid type error.
how to solve it
go binary
json
others

gob: type not registered for interface: map[string]interface {}

gob fails to encode map[string]interface{}
gob: type not registered for interface: map[string]interface {}
http://play.golang.org/p/Si4hd8I0JE
package main
import (
"bytes"
"encoding/gob"
"encoding/json"
"fmt"
"log"
)
func CloneObject(a, b interface{}) []byte {
buff := new(bytes.Buffer)
enc := gob.NewEncoder(buff)
dec := gob.NewDecoder(buff)
err := enc.Encode(a)
if err != nil {
log.Panic("e1: ", err)
}
b1 := buff.Bytes()
err = dec.Decode(b)
if err != nil {
log.Panic("e2: ", err)
}
return b1
}
func main() {
var a interface{}
a = map[string]interface{}{"X": 1}
b2, err := json.Marshal(&a)
fmt.Println(string(b2), err)
var b interface{}
b1 := CloneObject(&a, &b)
fmt.Println(string(b1))
}
Is it possible to encode map[string]interface{} in gob?
I am able to encode it with JSON
add
gob.Register(map[string]interface{}{})
http://play.golang.org/p/Dd3IzJgl0A
Probably yes, but you do have to Register your type beforehand. See http://golang.org/pkg/encoding/gob/#Register.
The details are documented in http://golang.org/pkg/encoding/gob/#hdr-Encoding_Details
(It really does help to look at the Go documentation :-)

Resources