2015-04-17 14 views
5

La codifica JSON out of the box in Go è davvero bella, ma ho bisogno di ottenere l'output per abbinare un determinato formato aggiungendo un livello. Ho capito un modo, ma speravo che ci sarebbe stato un modo più semplice di come lo sto facendo io.C'è un modo più semplice per aggiungere un livello su un oggetto JSON usando la codifica Golang JSON?

Di seguito è riportato un esempio di come lo sto facendo.

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 
} 

Ho un sacco di strutture definite che avrei bisogno di fare questo per, quindi spero in una soluzione migliore che non mi lascerà con con 100 linee di codice per aggiungere solo un wrapper intorno all'oggetto JSON. Idealmente mi piacerebbe qualcosa che possa raggiungere il massimo con il nome dell'elemento XML definito per l'encoder XML e utilizzarlo per avvolgere il JSON.

Nel mio caso sto utilizzando le funzioni MarshalJSON perché queste strutture possono essere annidate. Se aiuta, so sempre che la struttura della query è la struttura principale.

risposta

2

Forse mi manca qualcosa, ma è questo quello che stai cercando per?

Ho iniziato con la stessa idea di @Manawasp (utilizzando un'interfaccia map [string] {}) ma ho deciso di provare a ottenere il nome dal tag struct come mi hai chiesto ... ecco cosa mi è venuto in mente (* nota: ci possono essere casi errore non gestito, e questo può complicare qualcosa che può essere gestito abbastanza facilmente con l'altra soluzione)

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

} 

USCITA

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

EDIT
Ok, ecco un aggiornamento, ora che posso vedere che cosa il vostro problema è. Potrebbe essere brutto, e potrebbe non essere a prova di proiettile (gestione degli errori, ecc) ... ma per il mio test sembra fare quello che vuoi.

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

} 

ABBASTANZA-STAMPATO 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 
          } 
         } 
        ] 
       } 
      } 
     ] 
    } 
} 
+0

Questo è molto vicino. Sfortunatamente ho le situazioni in cui queste strutture sono annidate. Quindi un output di esempio è '{" query ": {" query ": [{" term-query ": {" testo ": [" dati "]}}]}}'. Questo è il motivo per cui utilizzo le funzioni MarshalJSON. – justdewit

+0

@justdewit Visualizza la risposta modificata. Fammi sapere se questo affronta la tua domanda originale in modo più accurato. – sberry

+0

Grazie! Molto meglio di quello che stavo facendo. – justdewit

2

Quando ho iniziato a utilizzare Go & Json ho avuto lo stesso problema. Ho risolto entro tale

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

Idealmente, rinominare il metodo wrapJSON-wrap che restituiscono un'interfaccia e dopo convertire questa interfaccia per JSON o XML

+0

Grazie. È più pulito di quello che ho nella mia funzione. Speravo in una soluzione che potesse aiutare con gli oggetti annidati, però. Questo è il motivo per cui stavo usando le funzioni MarshalJSON, poiché TermQuery poteva essere una delle Query della struttura Query. – justdewit

Problemi correlati