Embed struct but only have certain fields in json - go

I have a struct that I want to embed but want to json encode only certain fields of that struct (and lowercase them). Is that possible?
https://play.golang.org/p/bEC4zlx2oC:
package main
import (
"encoding/json"
"fmt"
"net/url"
)
type MyStruct struct {
Name string `json:"name"`
*url.URL
}
func main() {
m := &MyStruct{
"Bob",
&url.URL{
Scheme: "http",
},
}
j, err := json.Marshal(m)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(j)) // want {"name":"Bob","scheme":"http"}
}

Expanding my comment with example.
It is feasible, you have to implement Marshal interface.
For example:
func (u *MyStruct) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Name string `json:"name"`
Scheme string `json:"scheme"`
}{
Name: u.Name,
Scheme: u.Scheme,
})
}
Play Link: https://play.golang.org/p/LLchuOdYvf
Output:
{"name":"Bob","scheme":"http"}

Related

How to extract data from map[string]interface{}?

I am sending the data to the API like following:
{"after": {"amount": 811,"id":123,"status":"Hi"}, "key": [70]}
and i am getting following while printing :
map[after:map[amount:811 id:123 status:Hi ] key:[70]]
Is there any way to print individual field like this??
amount::800
id:123
status:Hi
The code:
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
)
var (
PORT = ":8080"
)
func main() {
fmt.Println("In Main")
http.HandleFunc("/", changedData)
http.ListenAndServe(PORT, nil)
}
type Data struct {
Id int64 `json:"id"`
Amount float64 `json:"amount"`
Status string `json:"status"`
}
type mark map[string]interface{}
func changedData(w http.ResponseWriter, r *http.Request) {
fmt.Println("Coming From API")
reqBody, _ := ioutil.ReadAll(r.Body)
fmt.Println("Data coming from API ", string(reqBody))
digit := json.NewDecoder(strings.NewReader(string(reqBody)))
for digit.More() {
var result mark
err := digit.Decode(&result)
if err != nil {
if err != io.EOF {
log.Fatal(err)
}
break
}
fmt.Println("final_data ", result)
}
}
Decode to a Go type that matches the structure of the JSON document. You declared a type for the "after" field. Wrap that type with a struct to match the document.
func changedData(w http.ResponseWriter, r *http.Request) {
var v struct{ After Data }
err := json.NewDecoder(r.Body).Decode(&v)
if err != nil {
http.Error(w, "bad request", 400)
return
}
fmt.Printf("final_data: %#v", v.After)
}
Playground example.
I think you can define a struct type if you know the JSON file format or if the JSON format is predefined. As far as I know that mostly using interface{} is a way when you don't know the JSON format or there is no predefined format of the JSON. If you define a struct type  and use it while unmarshaling the JSON to struct, you can access the variables by typing like data.Id or data.Status.
Here's an example code:
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
AfterData After `json:"after"`
Key []int `json:"key"`
}
type After struct {
Id int64 `json:"id"`
Amount float64 `json:"amount"`
Status string `json:"status"`
}
func main() {
j := []byte(`{"after": {"amount": 811,"id":123,"status":"Hi"}, "key": [70]}`)
var data *Data
err := json.Unmarshal(j, &data)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(data.AfterData)
fmt.Println(data.AfterData.Id)
fmt.Println(data.AfterData.Amount)
fmt.Println(data.AfterData.Status)
}
Output will be
{123 811 Hi}
123
811
Hi
Go Playground

How can I convert from big.Rat to a string without specifying precision?

I have an endpoint implemented in golang that can receive amounts with different precision length, ie:
"123"
"123.12"
"123.123123"
I'm using big.Rat internally to deal with these numbers as follows:
import (
"encoding/json"
"math/big"
)
type MyStruct struct {
Amount big.Rat `json:"amount"`
}
func (mystr *MyStruct) MarshalJSON() ([]byte, error) {
type Alias MyStruct
return json.Marshal(&struct {
Amount json.Number `json:"amount"`
*Alias
}{
Amount: json.Number(mystr.Amount.FloatString(2)),
Alias: (*Alias)(mystr),
})
}
func (mystr *MyStruct) UnmarshalJSON(data []byte) error {
type Alias MyStruct
aux := &struct {
Amount json.Number `json:"amount"`
*Alias
}{
Alias: (*Alias)(mystr),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
mystr.Amount = mystr.toRat(aux.Amount.String())
return nil
}
func (mystr *MyStruct) toRat(val string) big.Rat {
ratAmount := new(big.Rat)
ratAmount.SetString(val)
return *ratAmount
}
My problem & question is related to the Marshal method, in particular with this line:
Amount: json.Number(mystr.Amount.FloatString(2)),
Because if the amount in the json has a number with more than two decimal places a rounding takes place and I don't want that, I just want to mantain exactly the same number that was
received when I did the Unmarshal method.
This is an example of rounding: https://play.golang.org/p/U6oi_aGc8lE
Is there a way to convert from big.Rat to string without defining the precision?
Thank you very much in advance!
This should be help:
package main
import (
"fmt"
"math/big"
"strconv"
)
func main() {
n := new(big.Rat)
n.SetString("34.999999")
x,_ := n.Float64()
fmt.Println("Result: "+strconv.FormatFloat(x, 'f', -1, 64))
}

Can I set tag dynamically on runtime? [duplicate]

I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.

How do I dynamically change the struct's json tag?

I have the following:
package main
import (
"encoding/json"
"fmt"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
for i := 0; i < value.NumField(); i++ {
tag := value.Type().Field(i).Tag.Get("json")
field := value.Field(i)
fmt.Println(tag, field)
}
return json.Marshal(u)
}
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
I am trying to json encode the struct but before I do I need to change the json key...eg the final json should look like:
{"id": 1, "name": "Ken Jennings", "address": "123 Jennings Street"}
I noticed the method for value.Type().Field(i).Tag.Get("json"), however there is no setter method. Why? and how do I get the desired json output.
Also, how do I iterate through all the fields, including the embedded struct Another?
https://play.golang.org/p/Qi8Jq_4W0t
Since Go 1.8 you can use this solution:
func main() {
anoth := Another{"123 Jennings Street"}
_ = json.NewEncoder(os.Stdout).Encode(
&User{1, "Ken Jennings", "name",
anoth},
)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
tag string `json:"-"`
Another
}
type Another struct {
Address string `json:"address"`
}
func (u *User) MarshalJSON() ([]byte, error) {
type alias struct {
ID int64 `json:"id"`
Name string `json:"name"`
tag string `json:"-"`
Another
}
var a alias = alias(*u)
return json.Marshal(&a)
}
Will give us:
{"id":1,"name":"Ken Jennings","address":"123 Jennings Street"}
This solution made possible by the fact that in Go 1.8 you can assign structs with same structure but different tags to each other. As you see type alias has the same fields as type User but with different tags.
It's kludgy, but if you can wrap the struct in another, and use the new one for encoding, then you could:
Encode the original struct,
Decode it to an interface{} to get a map
Replace the map key
Then encode the map and return it
Thus:
type MyUser struct {
U User
}
func (u MyUser) MarshalJSON() ([]byte, error) {
// encode the original
m, _ := json.Marshal(u.U)
// decode it back to get a map
var a interface{}
json.Unmarshal(m, &a)
b := a.(map[string]interface{})
// Replace the map key
b["name"] = b["first"]
delete(b, "first")
// Return encoding of the map
return json.Marshal(b)
}
In the playground: https://play.golang.org/p/TabSga4i17
You can create a struct copy w/ new tag name by using reflect.StructOf and reflect.Value.Convert function
https://play.golang.org/p/zJ2GLreYpl0
func (u *User) MarshalJSON() ([]byte, error) {
value := reflect.ValueOf(*u)
t := value.Type()
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
fmt.Println(t.Field(i).Tag)
sf = append(sf, t.Field(i))
if t.Field(i).Name == "Name" {
sf[i].Tag = `json:"name"`
}
}
newType := reflect.StructOf(sf)
newValue := value.Convert(newType)
return json.Marshal(newValue.Interface())
}
It seems the tag property of type User in the question is meant to be used for renaming the JSON fieldname for the Name property.
An implementation of MarshalJSON with a bit of reflection can do the job without that additional tag property and also without an additional wrapper struct (as suggested in the accepted answer), like so:
package main
import (
"encoding/json"
"os"
"reflect"
)
type User struct {
ID int64 `json:"id"`
Name string `json:"first"` // want to change this to `json:"name"`
Another
}
type Another struct {
Address string `json:"address"`
}
// define the naming strategy
func (User) SetJSONname(jsonTag string) string {
if jsonTag == "first"{
return "name"
}
return jsonTag
}
// implement MarshalJSON for type User
func (u User) MarshalJSON() ([]byte, error) {
// specify the naming strategy here
return marshalJSON("SetJSONname", u)
}
// implement a general marshaler that takes a naming strategy
func marshalJSON(namingStrategy string, that interface{}) ([]byte, error) {
out := map[string]interface{}{}
t := reflect.TypeOf(that)
v := reflect.ValueOf(that)
fnctn := v.MethodByName(namingStrategy)
fname := func(params ...interface{}) string {
in := make([]reflect.Value, len(params))
for k, param := range params {
in[k] = reflect.ValueOf(param)
}
return fnctn.Call(in)[0].String()
}
outName := ""
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
switch n := f.Tag.Get("json"); n {
case "":
outName = f.Name
case "-":
outName = ""
default:
outName = fname(n)
}
if outName != "" {
out[outName] = v.Field(i).Interface()
}
}
return json.Marshal(out)
}
func main() {
anoth := Another{"123 Jennings Street"}
u := User{1, "Ken Jennings", anoth,}
e := json.NewEncoder(os.Stdout)
e.Encode(u)
}
This will print:
{"Another":{"address":"123 Jennings Street"},"id":1,"name":"Ken Jennings"}
However, be aware that MarshalJSON always sorts the JSON tags, while the standard encoder preserves the order of struct fields.

How to marshal struct when some members are protected/inner/hidden

How do ensure the fields in this LookupCode struct are included when marshalling?
package main
import (
"encoding/json"
"fmt"
)
type LookupCode struct {
code string `json:"code"`
name string `json:"name"`
}
func (l *LookupCode) GetCode() string {
return l.code
}
func main() {
c := &LookupCode{
code: "A",
name: "Apple",
}
b, _ := json.MarshalIndent(c, "", "\t")
fmt.Println(string(b))
}
http://play.golang.org/p/my52DAn0-Z
You can by implementing the json.Marshaller interface:
Full Example: http://play.golang.org/p/8mIcPwX92P
// Implement json.Unmarshaller
func (l *LookupCode) UnmarshalJSON(b []byte) error {
var tmp struct {
Code string `json:"code"`
Name string `json:"name"`
}
err := json.Unmarshal(b, &tmp)
if err != nil {
return err
}
l.code = tmp.Code
l.name = tmp.Name
return nil
}
func (l *LookupCode) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Code string `json:"code"`
Name string `json:"name"`
}{
Code: l.code,
Name: l.name,
})
}
encode/json cannot marshal unexported fields. Change your code to:
type LookupCode struct {
Code string `json:"code"`
Name string `json:"name"`
}
and do the same wherever you use code or name.
Playground: http://play.golang.org/p/rak0nVCNGI
Edit
The limitation is due to the reflection used when marshalling the struct. If you need to keep your values unexported, you must implement the json.Marshaller interface and do the encoding manually.
if the struct has only string-type fields,you can try this hack way.
package main
import (
"fmt"
"reflect"
"github.com/bitly/go-simplejson"
)
type A struct {
name string `json:"name"`
code string `json:"code"`
}
func marshal(a A) ([]byte, error) {
j := simplejson.New()
va := reflect.ValueOf(&a)
vt := va.Elem()
types := reflect.TypeOf(a)
for i := 0; i < vt.NumField(); i++ {
j.Set(types.Field(i).Tag.Get("json"), fmt.Sprintf("%v", reflect.Indirect(va).Field(i)))
}
return j.MarshalJSON()
}
func main() {
a := A{name: "jessonchan", code: "abc"}
b, _ := marshal(a)
fmt.Println(string(b))
}

Resources