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

时间:2015-04-17 02:15:41

标签: json go

Go中的开箱即用JSON编码非常好,但我需要通过添加图层来使输出与特定格式匹配。我已经找到了办法,但希望有一种比我做这种方式更简单的方法。

以下是我如何做的示例。

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
}

我有很多已定义的结构,我需要这样做,所以我希望有一个更好的解决方案,不会让我有100多行代码来添加一个包装器围绕JSON对象。理想情况下,我希望能够在为XML编码器定义的XML元素名称处达到顶峰,并使用它来包装JSON。

在我的情况下,我使用MarshalJSON函数,因为这些结构可以嵌套。如果有帮助我总是知道Query结构是根结构。

3 个答案:

答案 0 :(得分:5)

当我开始使用Go& Json我遇到了同样的问题。我解决了这个问题

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

理想情况下,将方法wrapJSON重命名为wrap,返回接口并将此接口转换为JSON或XML

答案 1 :(得分:2)

也许我错过了一些东西,但这就是你要找的东西吗?

我开始时使用与@Manawasp相同的想法(使用map [string] interface {})但是我决定尝试从结构标记中获取名称,就像你问的那样......这就是我想出的结果( *注意:可能存在未处理的错误情况,这可能会使用其他解决方案很容易处理的内容过于复杂化)

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

}

<强>输出

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

修改
好的,现在这是一个更新,我可以看到你的问题是什么。它可能是丑陋的,它可能不是防弹(错误处理等)......但是对于我的测试它似乎做你想要的。

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 :(得分:0)

type MultiMatch struct {
    Query              string   `json:"query"`
    Fields             []string `json:"fields"`
}

func (m *MultiMatch) MarshalJSON() ([]byte, error) {
    w := map[string]interface{}{}
    w["multi_match"] = *m
    return json.Marshal(w)
}