Go'da bir haritaya kısmen JSON unmarshal


105

Websocket sunucum JSON verilerini alacak ve dağıtımını kaldıracak. Bu veriler her zaman anahtar / değer çiftleri olan bir nesneye sarılır. Anahtar dizesi, Go sunucusuna ne tür bir değer olduğunu söyleyerek değer tanımlayıcı görevi görür. Ne tür bir değer olduğunu bilerek, JSON değerinin doğru yapı türüne göre ayrıştırılmasına geçebilirim.

Her json nesnesi birden çok anahtar / değer çifti içerebilir.

Örnek JSON:

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

"encoding/json"Bunu yapmak için paketi kullanmanın kolay bir yolu var mı ?

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

Her türlü öneri / yardım için teşekkürler!

Yanıtlar:


201

Bu, Unmarshaling ile bir map[string]json.RawMessage.

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

Daha fazla ayrıştırmak için aşağıdaki sendMsggibi bir şey yapabilirsiniz:

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

Çünkü say, aynı şeyi yapabilir ve bir dizeye ayırabilirsiniz:

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

DÜZENLEME: SendMsg yapınızdaki değişkenleri doğru bir şekilde unmarshal yapmak için dışa aktarmanız gerekeceğini unutmayın. Yapı tanımınız şöyle olacaktır:

type sendMsg struct {
    User string
    Msg  string
}

Örnek: https://play.golang.org/p/OrIjvqIsi4-


6
Mükemmel! Nasıl kullanabileceğini özledim RawMessage. Tam olarak ihtiyacım olan şey. Hakkında say, aslında onu hala istiyorum json.RawMessage, çünkü dizge hala çözülmedi (sarmalama "ve \nkaçma karakterleri, vb.), Bu yüzden onu da geri alacağım.
ANisus

1
Cevabımı yaptığınla eşleşecek şekilde düzelttim. Teşekkürler
Stephen Weinberg

3
Tür, bunun yerine map [string] * json.RawMessage olmalıdır çünkü Unmarshal / Marshal yöntemleri json.RawMessage üzerinde uygulanmaz.
albert

1
@albert, benim için çalışıyor: play.golang.org/p/XYsozrJrSl . Ancak, bir işaretçi kullanmanın daha iyi olacağı konusunda haklısınız. Kodumun tersi düzgün çalışmıyor: play.golang.org/p/46JOdjPpVI . Bir işaretçi kullanmak sorunu düzeltir: play.golang.org/p/ZGwhXkYUT3 .
Stephen Weinberg

3
* Json.RawMessage'a güncelledikten sonra, şimdi json.Unmarshal'a yapılan çağrılarda bunlardan vazgeçmeniz gerekir.
Kyle Lemons

2

Stephen Weinberg'in cevabına ek olarak, o zamandan beri var olan bir nesneye verileri kolayca doldurmanın yanı sıra mevcut nesneyi bir JSON dizesine kodlamaya yardımcı olan iojson adlı kullanışlı bir araç uyguladım . Diğer ara yazılımlarla çalışmak için bir iojson ara yazılım da sağlanır. Daha fazla örnek https://github.com/junhsieh/iojson adresinde bulunabilir.

Misal:

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

Örnek çıktı:

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

2

İşte benzer bir şey yapmanın zarif bir yolu. Ama neden JSON kısmen unmarshal yapıyor? Bu mantıklı değil.

  1. Sohbet için yapılarınızı oluşturun.
  2. Struct için json kodunu çöz.
  3. Artık Struct / Object içindeki her şeye kolayca erişebilirsiniz.

Aşağıdaki çalışma koduna bakın. Kopyalayıp yapıştırın.

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

Oyun alanına git


1
Geçici nesneler olarak birkaç yüz iç içe geçmiş alandan oluşan yapılarla çalışırken kısmi unmarshalling gereklidir. Örneğin, sunucudan json alın, tek alanları güncelleyin ve sunucuya geri gönderin.
Artem Baskakov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.