En partie JSON unmarshal dans une carte dans Go

98

Mon serveur websocket recevra et annulera les données JSON. Ces données seront toujours enveloppées dans un objet avec des paires clé / valeur. La chaîne de clé agira comme identificateur de valeur, indiquant au serveur Go de quel type de valeur il s'agit. En connaissant le type de valeur, je peux ensuite procéder à la décompression JSON de la valeur dans le type de structure correct.

Chaque objet json peut contenir plusieurs paires clé / valeur.

Exemple JSON:

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

Existe-t-il un moyen simple d'utiliser le "encoding/json"package pour ce faire?

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

Merci pour tout type de suggestion / aide!

ANisus
la source

Réponses:

195

Cela peut être accompli en Unmarshaling dans un fichier map[string]json.RawMessage.

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

Pour analyser plus en détail sendMsg, vous pouvez alors faire quelque chose comme:

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

Pour say, vous pouvez faire la même chose et unmarshal dans une chaîne:

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

EDIT: Gardez à l'esprit que vous devrez également exporter les variables de votre structure sendMsg vers unmarshal correctement. Donc, votre définition de structure serait:

type sendMsg struct {
    User string
    Msg  string
}

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

Stephen Weinberg
la source
6
Parfait! J'ai manqué comment vous pourriez utiliser RawMessage. Exactement ce dont j'avais besoin. À propos say, je le veux toujours tel quel json.RawMessage, car la chaîne n'est toujours pas décodée (caractères enveloppants "et échappés \n, etc.), je vais donc la démarseler aussi.
ANisus
1
J'ai corrigé ma réponse pour correspondre à ce que vous avez fait. Merci
Stephen Weinberg
3
Le type doit être map [string] * json.RawMessage à la place car les méthodes Unmarshal / Marshal ne sont pas implémentées sur json.RawMessage.
albert
1
@albert, fonctionne pour moi: play.golang.org/p/XYsozrJrSl . Cependant, vous avez raison de dire que l'utilisation d'un pointeur serait préférable. L'inverse de mon code ne fonctionne pas correctement: play.golang.org/p/46JOdjPpVI . L'utilisation d'un pointeur le corrige: play.golang.org/p/ZGwhXkYUT3 .
Stephen Weinberg
3
Après avoir mis à jour vers * json.RawMessage, vous devez maintenant les déréférencer dans les appels à json.Unmarshal.
Kyle Lemons
2

Suite à la réponse de Stephen Weinberg, j'ai depuis implémenté un outil pratique appelé iojson , qui permet de renseigner facilement les données d'un objet existant et d'encoder l'objet existant en une chaîne JSON. Un middleware iojson est également fourni pour fonctionner avec d'autres middlewares. Plus d'exemples peuvent être trouvés sur https://github.com/junhsieh/iojson

Exemple:

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

Exemple de sortie:

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
Jun Xie
la source
1

Voici une manière élégante de faire la même chose. Mais pourquoi en partie JSON unmarshal? Cela n'a pas de sens.

  1. Créez vos structures pour le chat.
  2. Décodez json en Struct.
  3. Vous pouvez désormais accéder facilement à tout dans Struct / Object.

Regardez ci-dessous le code de travail. Copiez et collez-le.

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

Aller au terrain de jeu

Taban Cosmos
la source
1
une suppression partielle est nécessaire lorsque vous travaillez avec des structures de plusieurs centaines de champs imbriqués en tant qu'objets temporaires. Par exemple, récupérez json du serveur, mettez à jour des champs uniques et publiez-le sur le serveur.
Artem Baskakov