บางส่วนของ JSON unmarshal ลงในแผนที่ใน Go


105

เซิร์ฟเวอร์ websocket ของฉันจะรับและข้อมูล JSON ที่ไม่เป็นอันตราย ข้อมูลนี้จะรวมอยู่ในวัตถุที่มีคู่คีย์ / ค่าเสมอ คีย์สตริงจะทำหน้าที่เป็นตัวระบุค่าโดยบอกเซิร์ฟเวอร์ Go ว่าเป็นค่าประเภทใด เมื่อทราบว่าประเภทของค่าใดฉันจึงสามารถดำเนินการต่อ JSON ค่า unmarshal ลงในประเภทของโครงสร้างที่ถูกต้อง

json-object แต่ละตัวอาจมีคู่คีย์ / ค่าหลายคู่

ตัวอย่าง JSON:

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

มีวิธีง่ายๆในการใช้"encoding/json"แพ็คเกจนี้หรือไม่?

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

ขอบคุณสำหรับข้อเสนอแนะ / ความช่วยเหลือ!

คำตอบ:


201

สิ่งนี้สามารถทำได้โดย Unmarshaling เป็นไฟล์map[string]json.RawMessage.

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

หากต้องการแยกวิเคราะห์เพิ่มเติมsendMsgคุณสามารถทำสิ่งต่อไปนี้

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

สำหรับsayคุณสามารถทำสิ่งเดียวกันและ unmarshal เป็นสตริง:

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

แก้ไข:โปรดทราบว่าคุณจะต้องส่งออกตัวแปรในโครงสร้าง sendMsg ของคุณเป็น unmarshal อย่างถูกต้อง ดังนั้นนิยามโครงสร้างของคุณจะเป็น:

type sendMsg struct {
    User string
    Msg  string
}

ตัวอย่าง: https://play.golang.org/p/OrIjvqIsi4-


6
สมบูรณ์แบบ! ฉันพลาดวิธีที่คุณสามารถRawMessageใช้ได้ สิ่งที่ฉันต้องการ เกี่ยวกับsayฉันยังคงต้องการให้เป็นjson.RawMessageเพราะสตริงยังไม่ได้ถอดรหัส (การตัด"และ\nอักขระที่ใช้Escape ฯลฯ ) ดังนั้นฉันจะไม่เปิดเผยมันด้วย
ANisus

1
ฉันแก้ไขคำตอบให้ตรงกับสิ่งที่คุณทำ ขอบคุณ
Stephen Weinberg

3
ประเภทควรเป็น map [string] * json.RawMessage แทนเนื่องจากไม่ได้ใช้วิธี Unmarshal / Marshal บน json.RawMessage
albert

1
@albert ทำงานสำหรับฉัน: play.golang.org/p/XYsozrJrSl อย่างไรก็ตามคุณถูกต้องที่ใช้ตัวชี้จะดีกว่า ผกผันของรหัสของฉันทำงานไม่ถูกต้อง: play.golang.org/p/46JOdjPpVI โดยใช้ตัวชี้แก้ไขมันplay.golang.org/p/ZGwhXkYUT3
Stephen Weinberg

3
หลังจากที่คุณอัปเดตเป็น * json.RawMessage ตอนนี้คุณต้องยกเลิกการอ้างอิงในการเรียกไปที่ json.
Kyle Lemons

2

นอกเหนือจากคำตอบของ Stephen Weinberg แล้วฉันได้ใช้เครื่องมือที่มีประโยชน์ที่เรียกว่า iojsonซึ่งช่วยเติมข้อมูลไปยังวัตถุที่มีอยู่ได้อย่างง่ายดายรวมทั้งการเข้ารหัสวัตถุที่มีอยู่เป็นสตริง JSON นอกจากนี้ยังมีมิดเดิลแวร์ iojson เพื่อทำงานร่วมกับมิดเดิลแวร์อื่น ๆ สามารถดูตัวอย่างเพิ่มเติมได้ที่https://github.com/junhsieh/iojson

ตัวอย่าง:

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

ตัวอย่างผลลัพธ์:

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

นี่คือวิธีที่สวยงามในการทำสิ่งที่คล้ายกัน แต่เหตุใด JSON บางส่วนจึงไม่เป็นอันตราย นั่นไม่สมเหตุสมผล

  1. สร้างโครงสร้างของคุณสำหรับการแชท
  2. ถอดรหัส json ไปยังโครงสร้าง
  3. ตอนนี้คุณสามารถเข้าถึงทุกอย่างในโครงสร้าง / วัตถุได้อย่างง่ายดาย

ดูรหัสการทำงานด้านล่าง คัดลอกและวาง

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

ไปสนามเด็กเล่น


1
การลบล้างบางส่วนเป็นสิ่งที่จำเป็นเมื่อคุณทำงานกับโครงสร้างของฟิลด์ที่ซ้อนกันหลายร้อยฟิลด์เป็นวัตถุชั่วคราว ตัวอย่างเช่นรับ json จากเซิร์ฟเวอร์อัปเดตช่องเดียวและโพสต์กลับบนเซิร์ฟเวอร์
Artem Baskakov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.