Is there an easier way to add a layer on top of a JSON object using Golang JSON encoding?

Out of the box, the JSON encoding in Go is really good, but I need the result to fit a specific format by adding a layer. I understood the way, but I hoped that there would be an easier way than how I do it.

Below is an example of how I do this.

import ( "bytes" "encoding/json" "encoding/xml" "fmt" ) type Query struct { XMLName xml.Name `xml:"http://marklogic.com/appservices/search query" json:"-"` Format int `xml:"-" json:"-"` Queries []interface{} `xml:",any" json:"queries"` } type TermQuery struct { XMLName xml.Name `xml:"http://marklogic.com/appservices/search term-query" json:"-"` Terms []string `xml:"http://marklogic.com/appservices/search text" json:"text"` Weight float64 `xml:"http://marklogic.com/appservices/search weight,omitempty" json:"weight,omitempty"` } // use fakeQuery to avoid an infinite loop type fakeQuery Query //MarshalJSON for Query struct in a special way to add wraping {"query":...} func (q Query) MarshalJSON() ([]byte, error) { return wrapJSON(`query`, fakeQuery(q)) } // use fakeTermQuery to avoid an infinite loop type fakeTermQuery TermQuery //MarshalJSON for TermQuery struct in a special way to add wraping {"term-query":...} func (q TermQuery) MarshalJSON() ([]byte, error) { return wrapJSON(`term-query`, fakeTermQuery(q)) } func wrapJSON(name string, item interface{}) ([]byte, error) { var buffer bytes.Buffer b, err := json.Marshal(item) buffer.Write([]byte(`{"`)) buffer.Write([]byte(name)) buffer.Write([]byte(`":`)) buffer.Write(b) buffer.Write([]byte(`}`)) return buffer.Bytes(), err } 

I have many specific structures that I will need to make, so I hope for a better solution that will not leave me with 100+ lines of code, just to add a wrapper around the JSON object. Ideally, I would like something that could reach the top of the name of the XML element defined for the XML encoder and use it to wrap the JSON.

In my case, I use the MarshalJSON functions because these structures can be nested. If this helps, I always know that the request structure is the root structure.

+5
source share
2 answers

Maybe something is missing for me, but is that what you are looking for?

I started with the same idea as @Manawasp (using the [string] {} map interface), but decided to try to get the name from the struct tag as you requested ... this is what I came up with (* Note: there may be some unhandled errors, and this can lead to an excessive compromise with the fact that you can easily cope with another solution)

http://play.golang.org/p/qO6tDZjtXA

 package main import ( "fmt" "reflect" "strings" ) import ( "encoding/json" "encoding/xml" "errors" ) type Query struct { XMLName xml.Name `xml:"http://marklogic.com/appservices/search query" json:"-"` Field1 string Field2 int64 } type TermQuery struct { XMLName xml.Name `xml:"http://marklogic.com/appservices/search term-query" json:"-"` Field3 string Field4 int64 } func getXmlName(d interface{}, label string) (string, bool) { switch reflect.TypeOf(d).Kind() { case reflect.Struct: v, _ := reflect.TypeOf(d).FieldByName(label) parts := strings.Split(v.Tag.Get("xml"), " ") return parts[1], true } return "", false } func wrapJson(item interface{}) ([]byte, error) { if n, ok := getXmlName(item, "XMLName"); ok { b, err := json.Marshal(map[string]interface{}{n: item}) if err != nil { return nil, err } return b, nil } return nil, errors.New("You failed") } func main() { // create a Query and encode it as {"query": {struct}} q := Query{Field1: "hello", Field2: 42} wrappedQ, err := wrapJson(q) if err != nil { fmt.Println(err) return } fmt.Println(string(wrappedQ)) // create a TermQuery and encode it as {"term-query": {struct}} tq := TermQuery{Field3: "world", Field4: 99} wrappedTQ, err := wrapJson(tq) if err != nil { fmt.Println(err) return } fmt.Println(string(wrappedTQ)) } 

OUTPUT

 {"query":{"Field1":"hello","Field2":42}} {"term-query":{"Field3":"world","Field4":99}} 

EDIT
Ok, now the update, and I see what this problem is. It may be ugly, and it may not be bulletproof (error handling, etc.) ... but for my test, it seems to do what you want.

http://play.golang.org/p/8MloLP3X4H

 package main import ( "fmt" "reflect" "strings" ) import ( //"encoding/json" "encoding/json" "encoding/xml" "errors" ) type Query struct { XMLName xml.Name `xml:"http://marklogic.com/appservices/search query" json:"-"` Field1 string Field2 int64 Queries []interface{} `xml:",any" json:"queries"` } type TermQuery struct { XMLName xml.Name `xml:"http://marklogic.com/appservices/search term-query" json:"-"` Field3 string Field4 int64 } func getXmlName(d interface{}, label string) (string, bool) { switch reflect.TypeOf(d).Kind() { case reflect.Struct: v, _ := reflect.TypeOf(d).FieldByName(label) parts := strings.Split(v.Tag.Get("xml"), " ") return parts[1], true default: fmt.Println(reflect.TypeOf(d).Kind()) } return "", false } func wrapJson(item interface{}) (map[string]interface{}, error) { if n, ok := getXmlName(item, "XMLName"); ok { if k := reflect.ValueOf(item).FieldByName("Queries"); k.IsValid() { for i := 0; i < k.Len(); i++ { b, err1 := wrapJson(k.Index(i).Interface()) if err1 != nil { continue } k.Index(i).Set(reflect.ValueOf(b)) } } return map[string]interface{}{n: item}, nil } return nil, errors.New("You failed") } func asJson(i interface{}) []byte { b, err := json.Marshal(i) if err != nil { return []byte(`{"error": "too bad"}`) } return b } func main() { // create a TermQuery and encode it as {"term-query": {struct}} tq := TermQuery{Field3: "world", Field4: 99} wrappedTQ, err := wrapJson(tq) if err != nil { fmt.Println(err) return } fmt.Println(string(asJson(wrappedTQ))) // create a Query and encode it as {"query": {struct}} q := Query{ Field1: "hello", Field2: 42, Queries: []interface{}{ TermQuery{Field3: "world", Field4: 99}, TermQuery{Field3: "yay, it works!", Field4: 666}, Query{ Field1: "Hi", Field2: 21, Queries: []interface{}{ TermQuery{ Field3: "omg", Field4: 1, }, }, }, }, } wrappedQ, err := wrapJson(q) if err != nil { fmt.Println(err) return } fmt.Println(string(asJson(wrappedQ))) } 

PRETTY-PRINTED OUTOUT

 { "query": { "Field1": "hello", "Field2": 42, "queries": [ { "term-query": { "Field3": "world", "Field4": 99 } }, { "term-query": { "Field3": "yay, it works!", "Field4": 666 } }, { "query": { "Field1": "Hi", "Field2": 21, "queries": [ { "term-query": { "Field3": "omg", "Field4": 1 } } ] } } ] } } 
+2
source

When I started using Go and Json, I had the same problem. I decided that

 func wrapJSON(name string, item interface{}) ([]byte, error) { wrapped := map[string]interface{}{ name: item, } converted, err := json.Marshal(wrapped) return converted } 

Ideally, rename your wrapJSON method to wrap , which returns an interface even after converting that interface to JSON or XML

+2
source

All Articles