Idiomatic way to embed struct with custom MarshalJSON() method - json

Given the following structs:
type Person struct {
Name string `json:"name"`
}
type Employee struct {
*Person
JobRole string `json:"jobRole"`
}
I can easily marshal an Employee to JSON as expected:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Output:
{"name":"Bob","jobRole":"Sales"}
But when the embedded struct has a custom MarshalJSON() method...
func (p *Person) MarshalJSON() ([]byte,error) {
return json.Marshal(struct{
Name string `json:"name"`
}{
Name: strings.ToUpper(p.Name),
})
}
it breaks entirely:
p := Person{"Bob"}
e := Employee{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
Now results in:
{"name":"BOB"}
(Note the conspicuous lack of jobRole field)
This is easily anticipated... the embedded Person struct implements the MarshalJSON() function, which is being called.
The trouble is, it's not what I want. What I want would be:
{"name":"BOB","jobRole":"Sales"}
That is, encode Employee's fields normally, and defer to Person's MarshalJSON() method to marshal its fields, and hand back some tidy JSON.
Now I could add a MarshalJSON() method to Employee as well, but this requires that I know that the embedded type implements MarshalJSON() as well, and either (a) duplicate its logic, or (b) call Person's MarshalJSON() and somehow manipulate its output to fit where I want it. Either approach seems sloppy, and not very future proof (what if an embedded type I don't control some day adds a custom MarshalJSON() method?)
Are there any alternatives here that I haven't considered?

Don't put MarshalJSON on Person since that's being promoted to the outer type. Instead make a type Name string and have Name implement MarshalJSON. Then change Person to
type Person struct {
Name Name `json:"name"`
}
Example: https://play.golang.org/p/u96T4C6PaY
Update
To solve this more generically you're going to have to implement MarshalJSON on the outer type. Methods on the inner type are promoted to the outer type so you're not going to get around that. You could have the outer type call the inner type's MarshalJSON then unmarshal that into a generic structure like map[string]interface{} and add your own fields. This example does that but it has a side effect of changing the order of the final output fields
https://play.golang.org/p/ut3e21oRdj

Nearly 4 years later, I've come up with an answer that is fundamentally similar to #jcbwlkr's, but does not require the intermediate unmarshal/re-marshal step, by using a little bit of byte-slice manipulation to join two JSON segments.
func (e *Employee) MarshalJSON() ([]byte, error) {
pJSON, err := e.Person.MarshalJSON()
if err != nil {
return nil, err
}
eJSON, err := json.Marshal(map[string]interface{}{
"jobRole": e.JobRole,
})
if err != nil {
return nil, err
}
eJSON[0] = ','
return append(pJSON[:len(pJSON)-1], eJSON...), nil
}
Additional details and discussion of this approach here.

While this produces a different output than what the OP wants, I think it is still useful as a technique to prevent MarshalJSON of embedded structs from breaking the marshaling of structs that contain them.
There is a proposal for encoding/json to recognize an inline option in struct tags. If that ever gets implemented, then I think avoiding embedding structs for cases like in OP's example might be the best bet.
Currently, a reasonable workaround was described in a comment on the Go issue tracker and is the basis for this answer. It consists of defining a new type that will have the same memory layout as the original struct being embedded, but none of the methods:
https://play.golang.org/p/BCwcyIqv0F7
package main
import (
"encoding/json"
"fmt"
"strings"
)
type Person struct {
Name string `json:"name"`
}
func (p *Person) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Name string `json:"name"`
}{
Name: strings.ToUpper(p.Name),
})
}
// person has all the fields of Person, but none of the methods.
// It exists to be embedded in other structs.
type person Person
type EmployeeBroken struct {
*Person
JobRole string `json:"jobRole"`
}
type EmployeeGood struct {
*person
JobRole string `json:"jobRole"`
}
func main() {
{
p := Person{"Bob"}
e := EmployeeBroken{&p, "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
}
{
p := Person{"Bob"}
e := EmployeeGood{(*person)(&p), "Sales"}
output, _ := json.Marshal(e)
fmt.Printf("%s\n", string(output))
}
}
Outputs:
{"name":"BOB"}
{"name":"Bob","jobRole":"Sales"}
The OP wants {"name":"BOB","jobRole":"Sales"}. To achieve that, one would need to "inline" the object returned by Person.MarshalJSON into the object produced by Employee.MashalJSON, excluding the fields defined in Person.

I've used this approach on parent structs to keep the embedded struct from overriding marshaling:
func (e Employee) MarshalJSON() ([]byte, error) {
v := reflect.ValueOf(e)
result := make(map[string]interface{})
for i := 0; i < v.NumField(); i++ {
fieldName := v.Type().Field(i).Name
result[fieldName] = v.Field(i).Interface()
}
return json.Marshal(result)
}
It's handy but nests the embedded structs in the output::
{"JobRole":"Sales","Person":{"name":"Bob"}}
For a tiny struct like the one in the question, #Flimzy's answer is good but can be done more succinctly:
func (e Employee) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"jobRole": e.JobRole,
"name": e.Name,
})
}

A more generic way to support massive fields in both inner and outer fields.
The side effect is you need to write this for every outer structs.
Example: https://play.golang.org/p/iexkUYFJV9K
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
)
func Struct2Json2Map(obj interface{}) (map[string]interface{}, error) {
data, err := json.Marshal(obj)
if err != nil {
return nil, err
}
var kvs map[string]interface{}
err = json.Unmarshal(data, &kvs)
if err != nil {
return nil, err
}
return kvs, nil
}
type Person struct {
Name string `json:"-"`
}
func (p Person) MarshalJSONHelper() (map[string]interface{}, error) {
return Struct2Json2Map(struct {
Name string `json:"name"`
}{
Name: strings.ToUpper(p.Name),
})
}
type Employee struct {
Person
JobRole string `json:"jobRole"`
}
func (e Employee) MarshalJSON() ([]byte, error) {
personKvs, err := e.Person.MarshalJSONHelper()
if err != nil {
return nil, err
}
type AliasEmployee Employee
kvs, err := Struct2Json2Map(struct {
AliasEmployee
} {
AliasEmployee(e),
})
for k,v := range personKvs {
kvs[k] = v
}
return json.Marshal(kvs)
}
func main() {
bob := Employee{
Person: Person{
Name: "Bob",
},
JobRole: "Sales",
}
output, err := json.Marshal(bob)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(output))
}

Related

Unmarshalling of JSON with dynamic keys

I have a scenario where the JSON that has dynamic set of fields that need to get unmarshalled in to a struct.
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
type Status struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status map[string]Status `json:"status"`
}
func main() {
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var person Person
if err := dec.Decode(&person); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Println(person)
fmt.Println(person.Status["bvu62fu6dq"])
}
}
The output:
{john 23 map[]}
{false}
When it gets unmarshalled, the nested status struct is not being correctly resolved to the value in the JSON (shows false even with true value in JSON), is there any issue in the code?
Your types don't really match with the JSON you have:
type Status struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status map[string]Status `json:"status"`
}
Maps to JSON that looks something like this:
{
"name": "foo",
"age": 12,
"status": {
"some-string": {
"Status": true
}
}
}
The easiest way to unmarshal data with a mix of known/unknown fields in a go type is to have something like this:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"` // skip this key
}
Then, first unmarshal the known data:
var p Person
if err := json.Unmarshal([]byte(jsonStream), &p); err != nil {
panic(err)
}
// then unmarshal the rest of the data
if err := json.Unmarshal([]byte(jsonStream), &p.Random); err != nil {
panic(err)
}
Now the Random map will contain every and all data, including the name and age fields. Seeing as you've got those tagged on the struct, these keys are known, so you can easily delete them from the map:
delete(p.Random, "name")
delete(p.Random, "age")
Now p.Random will contain all the unknown keys and their respective values. These values apparently will be an object with a field status, which is expected to be a boolean. You can set about using type assertions and convert them all over to a more sensible type, or you can take a shortcut and marshal/unmarshal the values. Update your Person type like so:
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"`
Statuses map[string]Status `json:"-"`
}
Now take the clean Random value, marshal it and unmarshal it back into the Statuses field:
b, err := json.Marshal(p.Random)
if err != nil {
panic(err)
}
if err := json.Unmarshal(b, &p.Statuses); err != nil {
panic(err)
}
// remove Random map
p.Random = nil
The result is Person.Statuses["bvu62fu6dq"].Status is set to true
Demo
Cleaning this all up, and marshalling the data back
Now because our Random and Statuses fields are tagged to be ignored for JSON marshalling (json:"-"), marshalling this Person type won't play nice when you want to output the original JSON from these types. It's best to wrap this logic up in a custom JSON (un)-Marshaller interface. You can either use some intermediary types in your MarshalJSON and UnmarshalJSON methods on the Person type, or just create a map and set the keys you need:
func (p Person) MarshalJSON() ([]byte, error) {
data := make(map[string]interface{}, len(p.Statuses) + 2) // 2 being the extra fields
// copy status fields
for k, v := range p.Statuses {
data[k] = v
}
// add known keys
data["name"] = p.Name
data["age"] = p.Age
return json.Marshal(data) // return the marshalled map
}
Similarly, you can do the same thing for UnmarshalJSON, but you'll need to create a version of the Person type that doesn't have the custom handling:
type intermediaryPerson struct {
Name string `json:"name"`
Age int `json:"age"`
Random map[string]interface{} `json:"-"`
}
// no need for the tags and helper fields anymore
type Person struct {
Name string
Age int
Statuses map[string]Status // Status type doesn't change
}
func (p *Person) UnmarshalJSON(data []byte) error {
i := intermediaryPerson{}
if err := json.Unmarshal(data, &i); err != nil {
return err
}
if err := json.Unmarshal(data, &i.Random); err != nil {
return err
}
delete(i.Random, "name")
delete(i.Random, "age")
stat, err := json.Marshal(i.Random)
if err != nil {
return err
}
// copy known fields
p.Name = i.Name
p.Age = i.Age
return json.Unmarshal(stat, &p.Statuses) // set status fields
}
In cases like this, it's common to create a type that handles the known fields and embed that, though:
type BasePerson struct {
Name string `json:"name"`
Age int `json:"age"`
}
and embed that in both the intermediary and the "main"/exported type:
type interPerson struct {
BasePerson
Random map[string]interface{} `json:"-"`
}
type Person struct {
BasePerson
Statuses map[string]Status
}
That way, you can just unmarshal the known fields directly into the BasePerson type, assign it, and then handle the map:
func (p *Person) UnmarshalJSON(data []byte) error {
base := BasePerson{}
if err := json.Unmarshal(data, &base); err != nil {
return err
}
p.BasePerson = base // takes care of all known fields
unknown := map[string]interface{}{}
if err := json.Unmarshal(data, unknown); err != nil {
return err
}
// handle status stuff same as before
delete(unknown, "name") // remove known fields
// marshal unknown key map, then unmarshal into p.Statuses
}
Demo 2
This is how I'd go about it. It allows for calls to json.Marshal and json.Unmarshal to look just like any other type, it centralises the handling of unknown fields in a single place (the implementation of the marshaller/unmarshaller interface), and leaves you with a single Person type where every field contains the required data, in a usable format. It's a tad inefficient in that it relies on unmarshalling/marshalling/unmarshalling the unknown keys. You could do away with that, like I said, using type assertions and iterating over the unknown map instead, faffing around with something like this:
for k, v := range unknown {
m, ok := v.(map[string]interface{})
if !ok {
continue // not {"status": bool}
}
s, ok := m["status"]
if !ok {
continue // status key did not exist, ignore
}
if sb, ok := s.(bool); ok {
// ok, we have a status bool value
p.Statuses[k] = Status{
Status: sb,
}
}
}
But truth be told, the performance difference won't be that great (it's micro optimisation IMO), and the code is a tad too verbose to my liking. Be lazy, optimise when needed, not whenever
Type doesn't meet with your json value.
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
For above json your code should look like below snnipet to work (some modifications in your existing code).
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strings"
)
const jsonStream = `{
"name": "john",
"age": 23,
"bvu62fu6dq": {
"status": true
}
}`
type bvu62fu6dq struct {
Status bool
}
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Status bvu62fu6dq `json:"bvu62fu6dq"`
}
func main() {
dec := json.NewDecoder(strings.NewReader(jsonStream))
for {
var person Person
if err := dec.Decode(&person); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
fmt.Println(person)
fmt.Println(person.Status)
}
}
Based on your json data you have to map with type fields.
Run code snippet

Changing JSON tags in struct with custom MarshalJSON

We get some JSON input, unmarshal, perform some work, then marshal and ship off somewhere else. The JSON we get may have a field named "user". When we marshal back to JSON we need to have that field "user" changed to "username". We can do this by creating a new struct with all the same fields, but different JSON tags, but that seemed a bit cumbersome. I thought a custom marshaller would work here, but I'm a bit stuck. Consider the following code.
package main
import (
"encoding/json"
"fmt"
)
type StructA struct {
Username string `json:"user"`
Process string `json:"process"`
}
func main() {
var test1 StructA
err := json.Unmarshal([]byte(`{"user": "user123", "process": "something"}`), &test1)
if err != nil {
fmt.Println(err)
}
// do some work with test1
jsonByte, err := json.Marshal(&test1)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(jsonByte))
}
func (u *StructA) MarshalJSON() ([]byte, error) {
type Alias StructA
return json.Marshal(&struct {
Username string `json:"username"`
*Alias
}{
Username: u.Username,
Alias: (*Alias)(u),
})
}
https://play.golang.org/p/_w0rlQrcgrW
Ideally this would allow me to change the JSON tag on that field from "user" to "username". However, I get both "username" and "user".
{"username":"user123","user":"user123","process":"something"}
I certainly could create an entirely new struct that mirrors StructA with the tags I want, but I don't have to have to copy every single field and worry about keeping those two structs in sync. It's not the end of the world, but it doesn't seem like a good approach.
To be clear, the end result I'm looking for is the following:
{"username":"user123","process":"something"}
I'm sure I'm missing something trivial here, but it's been a long week and any assistance would be appreciated. Thanks!
One option could be to have one struct with the non-changing values and than 2 alternative structs which both include that struct and have only the changing values. You then use one for unmarshaling and the second one for marshaling.
type StructA struct {
Process string `json:"process"`
...
}
type WithUser struct {
StructA
Username `json:"user"`
}
type WithUsername struct {
StructA
Username `json:"username"`
}
This would require multiple structs but no duplication in each one and can be quite flexible in what you include, instead of hard coding what you want to change into a custom marshal function.
use reflect to create struct and change it's tag
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type StructA struct {
Username string `json:"user"`
Process string `json:"process"`
}
func main() {
var test1 StructA
err := json.Unmarshal([]byte(`{"user": "user123", "process": "something"}`), &test1)
if err != nil {
fmt.Println(err)
}
// do some work with test1
jsonByte, err := json.Marshal(&test1)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(jsonByte))
}
func (u *StructA) MarshalJSON() ([]byte, error) {
// get old struct fields
uType := reflect.TypeOf(u).Elem()
userNameField, _ := uType.FieldByName("Username")
// set username field tag
userNameField.Tag = `json:"username"`
processField, _ := uType.FieldByName("Process")
newType := reflect.StructOf([]reflect.StructField{userNameField, processField})
// set new value field
oldValue := reflect.ValueOf(u).Elem()
newtValue := reflect.New(newType).Elem()
for i := 0; i < oldValue.NumField(); i++ {
newtValue.Field(i).Set(oldValue.Field(i))
}
return json.Marshal(newtValue.Interface())
}

Is there a way to have json.Unmarshal() select struct type based on "type" property?

I have some JSON of the form:
[{
"type": "car",
"color": "red",
"hp": 85,
"doors": 4
}, {
"type": "plane",
"color": "blue",
"engines": 3
}]
I have types car and plane that satisfy a vehicle interface; I'd like to be able to write:
var v []vehicle
e := json.Unmarshal(myJSON, &v)
... and have JSON fill my slice of vehicles with a car and a plane; instead (and unsurprisingly) I just get "cannot unmarshal object into Go value of type main.vehicle".
For reference, here are suitable definitions of the types involved:
type vehicle interface {
vehicle()
}
type car struct {
Type string
Color string
HP int
Doors int
}
func (car) vehicle() { return }
type plane struct {
Type string
Color string
Engines int
}
func (plane) vehicle() { return }
var _ vehicle = (*car)(nil)
var _ vehicle = (*plane)(nil)
(Note that I'm actually totally uninterested in the t field on car and plane - it could be omitted because this information will, if someone successfully answers this question, be implicit in the dynamic type of the objects in v.)
Is there a way to have the JSON umarhsaller choose which type to use based on some part of the contents (in this case, the type field) of the data being decoded?
(Note that this is not a duplicate of Unmarshal JSON with unknown fields because I want each item in the slice to have a different dynamic type, and from the value of the 'type' property I know exactly what fields to expect—I just don't know how to tell json.Unmarshal how to map 'type' property values onto Go types.)
Taking the answers from the similar question: Unmarshal JSON with unknown fields, we can construct a few ways to unamrshal this JSON object in a []vehicle data structure.
The "Unmarshal with Manual Handling" version can be done by using a generic []map[string]interface{} data structure, then building the correct vehicles from the slice of maps. For brevity, this example does leave out the error checking for missing or incorrectly typed fields which the json package would have done.
https://play.golang.org/p/fAY9JwVp-4
func NewVehicle(m map[string]interface{}) vehicle {
switch m["type"].(string) {
case "car":
return NewCar(m)
case "plane":
return NewPlane(m)
}
return nil
}
func NewCar(m map[string]interface{}) *car {
return &car{
Type: m["type"].(string),
Color: m["color"].(string),
HP: int(m["hp"].(float64)),
Doors: int(m["doors"].(float64)),
}
}
func NewPlane(m map[string]interface{}) *plane {
return &plane{
Type: m["type"].(string),
Color: m["color"].(string),
Engines: int(m["engines"].(float64)),
}
}
func main() {
var vehicles []vehicle
objs := []map[string]interface{}{}
err := json.Unmarshal(js, &objs)
if err != nil {
log.Fatal(err)
}
for _, obj := range objs {
vehicles = append(vehicles, NewVehicle(obj))
}
fmt.Printf("%#v\n", vehicles)
}
We could leverage the json package again to take care of the unmarshaling and type checking of the individual structs by unmarshaling a second time directly into the correct type. This could all be wrapped up into a json.Unmarshaler implementation by defining an UnmarshalJSON method on the []vehicle type to first split up the JSON objects into raw messages.
https://play.golang.org/p/zQyL0JeB3b
type Vehicles []vehicle
func (v *Vehicles) UnmarshalJSON(data []byte) error {
// this just splits up the JSON array into the raw JSON for each object
var raw []json.RawMessage
err := json.Unmarshal(data, &raw)
if err != nil {
return err
}
for _, r := range raw {
// unamrshal into a map to check the "type" field
var obj map[string]interface{}
err := json.Unmarshal(r, &obj)
if err != nil {
return err
}
vehicleType := ""
if t, ok := obj["type"].(string); ok {
vehicleType = t
}
// unmarshal again into the correct type
var actual vehicle
switch vehicleType {
case "car":
actual = &car{}
case "plane":
actual = &plane{}
}
err = json.Unmarshal(r, actual)
if err != nil {
return err
}
*v = append(*v, actual)
}
return nil
}
JSON decoding and encoding in Go is actually surprisingly well at recognizing fields inside embedded structs. E.g. decoding or encoding the following structure works when there is no overlapping fields between type A and type B:
type T struct{
Type string `json:"type"`
*A
*B
}
type A struct{
Baz int `json:"baz"`
}
type B struct{
Bar int `json:"bar"`
}
Be aware that if both "baz" and "bar" are set in the JSON for the example above, both the T.A and T.B properties will be set.
If there is overlapping fields between A and B, or just to be able to better discard invalid combinations of fields and type, you need to implement the json.Unmarshaler interface. To not have to first decode fields into a map, you can extend the trick of using embedded structs.
type TypeSwitch struct {
Type string `json:"type"`
}
type T struct {
TypeSwitch
*A
*B
}
func (t *T) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &t.TypeSwitch); err != nil {
return err
}
switch t.Type {
case "a":
t.A = &A{}
return json.Unmarshal(data, t.A)
case "b":
t.B = &B{}
return json.Unmarshal(data, t.B)
default:
return fmt.Errorf("unrecognized type value %q", t.Type)
}
}
type A struct {
Foo string `json:"bar"`
Baz int `json:"baz"`
}
type B struct {
Foo string `json:"foo"`
Bar int `json:"bar"`
}
For marshaling back, json.Marshaler must also be implemented if there is overlapping fields.
Full example: https://play.golang.org/p/UHAdxlVdFQQ
The two passes approach works fine, but there is also the option of the mapstructure package, that was created to do exactly this.
I was facing the same problem.
I'm using the lib github.com/mitchellh/mapstructure together the encoding/json.
I first, unmarshal the json to a map, and use mapstructure to convert the map to my struct, e.g.:
type (
Foo struct {
Foo string `json:"foo"`
}
Bar struct {
Bar string `json:"bar"`
}
)
func Load(jsonStr string, makeInstance func(typ string) any) (any, error) {
// json to map
m := make(map[string]any)
e := json.Unmarshal([]byte(jsonStr), &m)
if e != nil {
return nil, e
}
data := makeInstance(m["type"].(string))
// decoder to copy map values to my struct using json tags
cfg := &mapstructure.DecoderConfig{
Metadata: nil,
Result: &data,
TagName: "json",
Squash: true,
}
decoder, e := mapstructure.NewDecoder(cfg)
if e != nil {
return nil, e
}
// copy map to struct
e = decoder.Decode(m)
return data, e
}
Using:
f, _ := Load(`{"type": "Foo", "foo": "bar"}`, func(typ string) any {
switch typ {
case "Foo":
return &Foo{}
}
return nil
})
If the property is a string you can use .(string) for casting the property because the origin is an interface.
You can use it the next way:
v["type"].(string)

Ignore JSON tags when marshalling

I am getting JSON data from an external source. The field names in this JSON are not something I want to carry with me, so I am converting them to names that make sense to me using the json:"originalname" tags.
When I marshal such an object back to JSON, I naturally get the ugly (original) names again.
Is there a way to ignore tags when marshalling? Or a way to specify a different name for marshall and unmarshall?
To clarify, I have prepared an example in the playground and pasted the same code below.
Thanks in advance.
package main
import (
"encoding/json"
"fmt"
)
type Band struct {
Name string `json:"bandname"`
Albums int `json:"albumcount"`
}
func main() {
// JSON -> Object
data := []byte(`{"bandname": "AC/DC","albumcount": 10}`)
band := &Band{}
json.Unmarshal(data, band)
// Object -> JSON
str, _ := json.Marshal(band)
fmt.Println("Actual Result: ", string(str))
fmt.Println("Desired Result:", `{"Name": "AC/DC","Albums": 10}`)
// Output:
// Actual Result: {"bandname":"AC/DC","albumcount":10}
// Desired Result: {"Name": "AC/DC","Albums": 10}
}
You could implement
type Marshaler interface {
MarshalJSON() ([]byte, error)
}
from the standard library's encoding/json package. Example:
type Band struct {
Name string `json:"bandname"`
Albums int `json:"albumcount"`
}
func (b Band) MarshalJSON() ([]byte, error) {
n, _ := json.Marshal(b.Name)
a, _ := json.Marshal(b.Albums)
return []byte(`{"Name":` + string(n) + `,"Albums":` + string(a) + `}`)
}
It's admittedly not a very nice solution, though.
As a generic solution, you could use reflection to create a new type that removes the json tags and then marshall that.
func getVariantStructValue(v reflect.Value, t reflect.Type) reflect.Value {
sf := make([]reflect.StructField, 0)
for i := 0; i < t.NumField(); i++ {
sf = append(sf, t.Field(i))
if t.Field(i).Tag.Get("json") != "" {
sf[i].Tag = ``
}
}
newType := reflect.StructOf(sf)
return v.Convert(newType)
}
func MarshalIgnoreTags(obj interface{}) ([]byte, error) {
value := reflect.ValueOf(obj)
t := value.Type()
newValue := getVariantStructValue(value, t)
return json.Marshal(newValue.Interface())
}
And you would just call it using:
str, _ := MarshalIgnoreTags(band)
Doing the opposite is a little trickier (ignore tags when unmarshalling JSON), but possible with mapstructure:
func UnmarshalIgnoreTags(data []byte, obj interface{}) error {
rv := reflect.ValueOf(obj)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return errors.New("unmarshal destination obj must be a non-nil pointer")
}
value := reflect.Indirect(rv)
t := value.Type()
newValue := getVariantStructValue(value, t)
i := newValue.Interface()
err := json.Unmarshal(data, &i)
if err == nil {
// We use mapstructure because i is of type map[string]interface{} and it's the easiest way to convert back to struct type
// See: https://stackoverflow.com/a/38939459/2516916
mapstructure.Decode(i, obj)
}
return err
}
See playground here: https://play.golang.org/p/XVYGigM71Cf
One could also use a library as mentioned in this thread: https://stackoverflow.com/a/50966527/5649638
This will give you a structs like this:
type TestJson struct {
Name string `json:"name" newtag:"newname"`
Age int `json:"age" newtag:"newage"`
}

Unmarshal on reflected value

Here is the code
package main
import (
"fmt"
"encoding/json"
"reflect"
)
var (
datajson []byte
//ref mapp
)
type mapp map[string]reflect.Type
type User struct {
Name string
//Type map[string]reflect.Type
}
func MustJSONEncode(i interface{}) []byte {
result, err := json.Marshal(i)
if err != nil {
panic(err)
}
return result
}
func MustJSONDecode(b []byte, i interface{}) {
err := json.Unmarshal(b, i)
if err != nil {
panic(err)
}
}
func Store(a interface{}) {
datajson = MustJSONEncode(a)
//fmt.Println(datajson)
}
func Get(a []byte, b interface{}) {
objType := reflect.TypeOf(b).Elem()
obj := reflect.New(objType)
//fmt.Println(obj)
MustJSONDecode(a, &obj)
fmt.Printf("%s", obj)
}
func main() {
dummy := &User{}
david := User{Name: "DavidMahon"}
Store(david)
Get(datajson, dummy)
}
In the Get function
func Get(a []byte, b interface{}) {
objType := reflect.TypeOf(b).Elem()
obj := reflect.New(objType)
//fmt.Println(obj)
MustJSONDecode(a, &obj)
fmt.Printf("%s", obj)
}
I am unable to unmarshal the json into the underlying object type.
Whats wrong here? I am so stuck here. Something very simple yet so difficult to figure out.
Thanks
UPDATE::Goal of this problem is to retreive a fully formed object of type passed in Get function.
The approach mentioned by Nick on the comment below doesnot get me the actual object which I already tried before. I can anyways retrieve the data (even when the object has recursive objects underneath) in a map like this
func Get(a []byte) {
var f interface{}
//buf := bytes.NewBuffer(a)
//v := buf.String()
//usr := &User{}
MustJSONDecode(a, &f)
fmt.Printf("\n %v \n", f)
}
However I need the actual object back not just the data. Something like user := &User{"SomeName"} where I need user object back from Unmarshall. The trick is somewhere in reflection but dont know how.
I'm confused as to why you want to do this, but here is how to fix it
func Get(a []byte, b interface{}) {
objType := reflect.TypeOf(b).Elem()
obj := reflect.New(objType).Interface()
//fmt.Println(obj)
MustJSONDecode(a, &obj)
fmt.Printf("obj = %#v\n", obj)
}
Note the call to Interface().
Playground link
It seems to me that you are going to a lot of trouble to make an empty &User when you already have one in b, eg
func Get(a []byte, b interface{}) {
MustJSONDecode(a, &b)
fmt.Printf("obj = %#v\n", b)
}
But I'm guessing there is some more to this plan which isn't apparent here!
reflect.New(objType) returns a reflect.Value Which is not the thing as the interface you passed. According to the docs for Value It is a struct with only unexported fields. the json package can't work with unexported fields. Since it's not the same object as you passed in and it's not even json encodable/decodable the json package will fail.
You will probably find the Laws of Reflection article useful while trying to use the reflect package.