2015-04-17 8 views
5

Die aus der Box JSON-Codierung in Go ist wirklich schön, aber ich brauche die Ausgabe zu erhalten, indem das Hinzufügen einer Schicht, die ein bestimmtes Format entsprechen. Ich habe einen Weg gefunden, aber ich hatte gehofft, dass es einen einfacheren Weg geben würde, als ich es tue.Gibt es einen einfacheren Weg, um eine Schicht über ein JSON-Objekt mit Golang JSON Kodierung hinzufügen?

Unten ist ein Beispiel dafür, wie ich es mache.

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 
} 

Ich habe eine Menge von definierten Strukturen, die ich brauchen würde, dies zu tun, so hoffe ich, für eine bessere Lösung, die mich nicht verlassen werden mit mit mehr als 100 Zeilen Code nur einen Wrapper hinzufügen um das JSON-Objekt herum. Idealerweise würde ich gerne etwas haben, das bei dem XML-Elementnamen, der für den XML-Codierer definiert ist, einen Spitzenwert erreichen könnte und diesen zum Umbrechen des JSON verwenden könnte.

In meinem Fall verwende ich die MarshalJSON Funktionen, weil diese Strukturen verschachtelt werden können. Wenn es hilft, weiß ich immer, dass die Abfragestruktur die Stammstruktur ist. Ich hatte das gleiche Problem

Antwort

2

Vielleicht ich bin etwas fehlt, aber das ist, was Sie suchen, zum?

Ich begann mit der gleichen Idee wie @Manawasp (mit einer Karte [string] Schnittstelle {}) aber entschied, zu versuchen, den Namen aus dem struct-Tag zu bekommen, wie Sie gefragt ... hier ist, was ich gefunden habe (* Anmerkung: es gibt nicht behandelte Fehlerfälle sein können, und dies kann etwas verkomplizieren, die ziemlich leicht mit der anderen Lösung gehandhabt werden kann)

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

BEARBEITEN
Ok, hier ist ein Update jetzt, dass ich sehen kann, was Ihr Problem ist. Es könnte hässlich sein, und es könnte nicht kugelsicher sein (Fehlerbehandlung, etc) ... aber für meinen Test scheint es zu tun, was Sie wollen.

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

Das ist sehr nah. Leider habe ich die Situationen, in denen diese Strukturen verschachtelt sind. So ein Beispiel für die Ausgabe ist '{ "Abfrage": { "Abfragen": [{ "Term-Abfrage": { "text": [ "Daten"]}}]}}'. Deshalb nutze ich die MarshalJSON-Funktionen. – justdewit

+0

@justdewit Siehe überarbeitete Antwort. Lassen Sie mich wissen, ob dies Ihre ursprüngliche Frage genauer angeht. – sberry

+0

Danke! Viel besser als das was ich gemacht habe. – justdewit

2

Als ich anfing Go & Json zu verwenden. Ich beschloss, es von diesem

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

Idealerweise Ihre Methode wrapJSON-wrap umbenennen, die eine Schnittstelle zurückkehren und nach der Konvertierung diese Schnittstelle zu JSON oder XML

+0

Dank. Das ist sauberer als das, was ich in meiner Funktion habe. Ich habe auf eine Lösung gehofft, die bei verschachtelten Artikeln helfen würde. Deshalb habe ich die MarshalJSON-Funktionen verwendet, da TermQuery der eine der Abfragen der Abfragestruktur sein könnte. – justdewit

Verwandte Themen