Sebagian JSON unmarshal menjadi peta di Go

98

Server websocket saya akan menerima dan membuka data JSON. Data ini akan selalu dibungkus dalam objek dengan pasangan kunci / nilai. Key-string akan bertindak sebagai pengenal nilai, memberi tahu server Go tentang jenis nilainya. Dengan mengetahui jenis nilai apa, saya kemudian dapat melanjutkan ke JSON menguraikan nilai ke dalam jenis struct yang benar.

Setiap json-object mungkin berisi beberapa pasangan kunci / nilai.

Contoh JSON:

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

Apakah ada cara mudah menggunakan "encoding/json"paket untuk melakukan ini?

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

Terima kasih atas saran / bantuan apa pun!

ANisus
sumber

Jawaban:

194

Ini dapat dilakukan dengan Unmarshaling menjadi a map[string]json.RawMessage.

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

Untuk mengurai lebih lanjut sendMsg, Anda dapat melakukan sesuatu seperti:

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

Karena say, Anda dapat melakukan hal yang sama dan mengurai menjadi string:

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

EDIT: Ingatlah bahwa Anda juga perlu mengekspor variabel di struct sendMsg Anda ke unmarshal dengan benar. Jadi definisi struct Anda adalah:

type sendMsg struct {
    User string
    Msg  string
}

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

Stephen Weinberg
sumber
6
Sempurna! Saya melewatkan bagaimana Anda bisa menggunakan RawMessage. Persis yang saya butuhkan. Tentang say, saya sebenarnya masih menginginkannya sebagai json.RawMessage, karena string masih belum didekodekan (membungkus "dan melarikan diri \n-karakter, dll), jadi saya akan membatalkannya juga.
ANisus
1
Saya memperbaiki jawaban saya agar sesuai dengan apa yang Anda lakukan. Terima kasih
Stephen Weinberg
3
Jenisnya harus map [string] * json.RawMessage sebagai gantinya karena metode Unmarshal / Marshal tidak diimplementasikan pada json.RawMessage.
albert
1
@albert, bekerja untuk saya: play.golang.org/p/XYsozrJrSl . Namun, Anda benar bahwa menggunakan penunjuk akan lebih baik. Kebalikan dari kode saya tidak bekerja dengan benar: play.golang.org/p/46JOdjPpVI . Menggunakan pointer memperbaikinya: play.golang.org/p/ZGwhXkYUT3 .
Stephen Weinberg
3
Setelah Anda memperbarui ke * json.RawMessage, Anda sekarang perlu mereferensikannya dalam panggilan ke json.Unmarshal.
Kyle Lemons
2

Lebih jauh ke jawaban Stephen Weinberg, saya telah menerapkan alat praktis yang disebut iojson , yang membantu untuk mengisi data ke objek yang ada dengan mudah serta mengkodekan objek yang ada ke string JSON. Sebuah middleware iojson juga disediakan untuk bekerja dengan middleware lain. Contoh lainnya dapat ditemukan di https://github.com/junhsieh/iojson

Contoh:

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 sampel:

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
sumber
1

Inilah cara elegan untuk melakukan hal serupa. Tetapi mengapa sebagian JSON tidak dapat dipercaya? Itu tidak masuk akal.

  1. Buat struct Anda untuk Obrolan.
  2. Decode json ke Struct.
  3. Sekarang Anda dapat mengakses semua yang ada di Struct / Object dengan mudah.

Lihat di bawah kode kerja. Salin dan tempel.

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

Pergilah bermain

Taban Cosmos
sumber
1
unmarshalling parsial diperlukan saat Anda bekerja dengan struktur dari beberapa ratus bidang bersarang sebagai objek temp. Misalnya, dapatkan json dari server, perbarui bidang tunggal dan poskan kembali di server.
Artem Baskakov