dongyanpai2701 2015-04-17 02:15
浏览 53
已采纳

有没有更简单的方法可以使用Golang JSON编码在JSON对象上添加图层?

The out of the box JSON encoding in Go is really nice, but I need to get the output to match a certain format by adding a layer. I've figured out a way, but was hoping that there would be an easier way than the way I'm doing it.

Below is an example of how I'm doing it.

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 a lot of defined structures that I would need to do this to, so I'm hoping for a better solution that won't leave me with with 100+ lines of code to just add a wrapper around the JSON object. Ideally I would like something that could peak at the XML element name defined for the XML encoder and use that to wrap the JSON.

In my case I'm using the MarshalJSON functions because these structures can be nested. If it helps I always know that the Query structure is the root structure.

  • 写回答

2条回答 默认 最新

  • duancanjiu3754 2015-04-17 04:37
    关注

    Perhaps I am missing something, but is this what you are looking for?

    I started off with the same idea as @Manawasp (using a map[string]interface{}) but decided to try to get the name from the struct tag like you asked about... here's what I came up with (*note: there may be unhandled error cases, and this may overcomplicate something that can be handled pretty easily with the other 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, here is an update now that I can see what your issue is. It might be ugly, and it might not be bullet-proof (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
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?
  • ¥15 c++头文件不能识别CDialog