簡體   English   中英

有沒有更簡單的方法可以使用 Golang JSON 編碼在 JSON object 上添加層?

[英]Is there an easier way to add a layer over a JSON object using Golang JSON Encoding?

開箱即用的 JSON 編碼在 Go 中非常好,但我需要通過添加層來獲得 output 以匹配某種格式。 我想出了一種方法,但希望有一種比我現在做的方法更簡單的方法。

下面是我如何做的一個例子。

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 object 周圍添加一個包裝器。 理想情況下,我希望在為 XML 編碼器定義的 XML 元素名稱處達到峰值,並使用它來包裝 JSON。

在我的例子中,我使用 MarshalJSON 函數,因為這些結構可以嵌套。 如果有幫助,我總是知道查詢結構是根結構。

當我開始使用 Go 和 Json 時,我遇到了同樣的問題。 我就這樣解決了

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

理想情況下,重命名您的方法wrapJSONwrap返回接口,然后將此接口轉換為 JSON 或 XML

也許我錯過了一些東西,但這就是你要找的嗎?

我從與@Manawasp 相同的想法開始(使用 map[string]interface{}),但決定嘗試從 struct 標簽中獲取名稱,就像你問的那樣......這就是我想出的(*注意:可能存在未處理的錯誤情況,這可能會使其他解決方案很容易處理的事情變得過於復雜)

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

編輯
好的,這是一個更新,我可以看到你的問題是什么。 它可能很丑陋,並且可能不是防彈的(錯誤處理等)......但對於我的測試,它似乎可以滿足您的需求。

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

}

漂亮的打印出來

{
    "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
                            }
                        }
                    ]
                }
            }
        ]
    }
}

在 JSON Marshaler 中,您可以使用各種 JSON 標記再定義一個(匿名)結構並添加所需的包裝。

由於您已經定義了 JSON Marshaler,因此在原始結構中定義的任何標簽都可能被您的 JSON Marshaler 實現覆蓋。

請參閱Golang 游樂場

type Book struct {
    Title     string
    Author    string
    Language  string
    Publisher string
}

func (b Book) MarshalJSON() ([]byte, error) {
    type BookDetailJSON struct {
        Name      string `json:"Title"`
        Author    string
        Language  string `json:",omitempty"`
        Publisher string `json:"-"`
    }
    type BookJSON struct {
        Book BookDetailJSON `json:"Novel"`
    }

    return json.Marshal(BookJSON{BookDetailJSON{
        Name:      b.Title,
        Author:    b.Author,
        Language:  b.Language,
        Publisher: b.Publisher,
    }})
}
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)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM