In parte unmarshal JSON in una mappa in Go


98

Il mio server websocket riceverà e annullerà il marshalling dei dati JSON. Questi dati verranno sempre inseriti in un oggetto con coppie chiave / valore. La stringa chiave fungerà da identificatore del valore, indicando al server Go che tipo di valore è. Sapendo quale tipo di valore, posso quindi procedere a JSON unmarshal il valore nel tipo corretto di struct.

Ogni oggetto json potrebbe contenere più coppie chiave / valore.

JSON di esempio:

{
    "sendMsg":{"user":"ANisus","msg":"Trying to send a message"},
    "say":"Hello"
}

Esiste un modo semplice per utilizzare il "encoding/json"pacchetto per farlo?

package main

import (
    "encoding/json"
    "fmt"
)

// the struct for the value of a "sendMsg"-command
type sendMsg struct {
    user string
    msg  string
}
// The type for the value of a "say"-command
type say string

func main(){
    data := []byte(`{"sendMsg":{"user":"ANisus","msg":"Trying to send a message"},"say":"Hello"}`)

    // This won't work because json.MapObject([]byte) doesn't exist
    objmap, err := json.MapObject(data)

    // This is what I wish the objmap to contain
    //var objmap = map[string][]byte {
    //  "sendMsg": []byte(`{"user":"ANisus","msg":"Trying to send a message"}`),
    //  "say": []byte(`"hello"`),
    //}
    fmt.Printf("%v", objmap)
}

Grazie per qualsiasi tipo di suggerimento / aiuto!

Risposte:


194

Questo può essere ottenuto Unmarshaling in un file map[string]json.RawMessage.

var objmap map[string]json.RawMessage
err := json.Unmarshal(data, &objmap)

Per analizzare ulteriormente sendMsg, potresti quindi fare qualcosa come:

var s sendMsg
err = json.Unmarshal(objmap["sendMsg"], &s)

Per say, puoi fare la stessa cosa e unmarshal in una stringa:

var str string
err = json.Unmarshal(objmap["say"], &str)

EDIT: tieni presente che dovrai anche esportare le variabili nella tua struttura sendMsg per unmarshal correttamente. Quindi la tua definizione della struttura sarebbe:

type sendMsg struct {
    User string
    Msg  string
}

Esempio: https://play.golang.org/p/OrIjvqIsi4-


6
Perfetto! Mi è mancato come potresti usare RawMessage. Esattamente quello di cui avevo bisogno. A proposito say, in realtà lo voglio ancora come json.RawMessage, perché la stringa non è ancora decodificata (caratteri di ritorno a capo "e di escape \n, ecc.), Quindi anch'io deselezionerò.
ANisus

1
Ho corretto la mia risposta in modo che corrispondesse a ciò che hai fatto. Grazie
Stephen Weinberg

3
Il tipo dovrebbe essere map [stringa] * json.RawMessage invece perché i metodi Unmarshal / Marshal non sono implementati su json.RawMessage.
albert

1
@albert, funziona per me: play.golang.org/p/XYsozrJrSl . Tuttavia, hai ragione sul fatto che sarebbe meglio usare un puntatore. L'inverso del mio codice non funziona correttamente: play.golang.org/p/46JOdjPpVI . L'uso di un puntatore lo risolve: play.golang.org/p/ZGwhXkYUT3 .
Stephen Weinberg

3
Dopo aver eseguito l'aggiornamento a * json.RawMessage, ora è necessario dereferenziarli nelle chiamate a json.Unmarshal.
Kyle Lemons

2

Oltre alla risposta di Stephen Weinberg, da allora ho implementato un pratico strumento chiamato iojson , che aiuta a popolare facilmente i dati in un oggetto esistente, nonché a codificare l'oggetto esistente in una stringa JSON. Viene fornito anche un middleware iojson per funzionare con altri middleware. Altri esempi possono essere trovati su https://github.com/junhsieh/iojson

Esempio:

func main() {
    jsonStr := `{"Status":true,"ErrArr":[],"ObjArr":[{"Name":"My luxury car","ItemArr":[{"Name":"Bag"},{"Name":"Pen"}]}],"ObjMap":{}}`

    car := NewCar()

    i := iojson.NewIOJSON()

    if err := i.Decode(strings.NewReader(jsonStr)); err != nil {
        fmt.Printf("err: %s\n", err.Error())
    }

    // populating data to a live car object.
    if v, err := i.GetObjFromArr(0, car); err != nil {
        fmt.Printf("err: %s\n", err.Error())
    } else {
        fmt.Printf("car (original): %s\n", car.GetName())
        fmt.Printf("car (returned): %s\n", v.(*Car).GetName())

        for k, item := range car.ItemArr {
            fmt.Printf("ItemArr[%d] of car (original): %s\n", k, item.GetName())
        }

        for k, item := range v.(*Car).ItemArr {
            fmt.Printf("ItemArr[%d] of car (returned): %s\n", k, item.GetName())
        }
    }
}

Output di esempio:

car (original): My luxury car
car (returned): My luxury car
ItemArr[0] of car (original): Bag
ItemArr[1] of car (original): Pen
ItemArr[0] of car (returned): Bag
ItemArr[1] of car (returned): Pen

1

Ecco un modo elegante per fare cose simili. Ma perché in parte JSON unmarshal? Non ha senso.

  1. Crea le tue strutture per la chat.
  2. Decodifica json in Struct.
  3. Ora puoi accedere facilmente a tutto in Struct / Object.

Guarda sotto il codice di lavoro. Copia e incolla.

import (
   "bytes"
   "encoding/json" // Encoding and Decoding Package
   "fmt"
 )

var messeging = `{
"say":"Hello",
"sendMsg":{
    "user":"ANisus",
    "msg":"Trying to send a message"
   }
}`

type SendMsg struct {
   User string `json:"user"`
   Msg  string `json:"msg"`
}

 type Chat struct {
   Say     string   `json:"say"`
   SendMsg *SendMsg `json:"sendMsg"`
}

func main() {
  /** Clean way to solve Json Decoding in Go */
  /** Excellent solution */

   var chat Chat
   r := bytes.NewReader([]byte(messeging))
   chatErr := json.NewDecoder(r).Decode(&chat)
   errHandler(chatErr)
   fmt.Println(chat.Say)
   fmt.Println(chat.SendMsg.User)
   fmt.Println(chat.SendMsg.Msg)

}

 func errHandler(err error) {
   if err != nil {
     fmt.Println(err)
     return
   }
 }

Vai al parco giochi


1
è necessario unmarshalling parziale quando si lavora con strutture di diverse centinaia di campi annidati come oggetti temporanei. Ad esempio, ottieni json dal server, aggiorna i singoli campi e pubblicalo di nuovo sul server.
Artem Baskakov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.