Частково JSON невстановлений на карту в Go


98

Мій веб-сокет-сервер буде отримувати та відключати дані JSON. Ці дані завжди будуть упаковані в об'єкт з парами ключ / значення. Рядок ключів буде діяти як ідентифікатор значення, повідомляючи серверу Go, яке це значення. Знаючи, який тип значення, я можу перейти до JSON демаршалізувати значення до правильного типу структури.

Кожен json-об'єкт може містити кілька пар ключ / значення.

Приклад 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)
}

Дякуємо за будь-яку пропозицію / допомогу!

Відповіді:


194

Цього можна здійснити, нерозмірявши в a 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ви можете зробити те саме і зняти маршал у рядок:

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

РЕДАГУВАТИ: Майте на увазі, вам також потрібно буде експортувати змінні у вашій структурі sendMsg для правильної демаршалізації. Отже, ваше визначення структури буде таким:

type sendMsg struct {
    User string
    Msg  string
}

Приклад: https://play.golang.org/p/OrIjvqIsi4-


6
Ідеально! Я пропустив, як ти міг використати RawMessage. Саме те, що мені було потрібно. Приблизно say, я насправді все ще хочу, щоб це було json.RawMessage, оскільки рядок все ще не декодується (обгортання "та екрановані \nсимволи тощо), тому я також демаршую це.
Аніс,

1
Я закріпив свою відповідь так, щоб вона відповідала вашому. Дякую
Стівен Вайнберг

3
Тип повинен бути map [рядок] * json.RawMessage, натомість, оскільки методи Unmarshal / Marshal не реалізовані на json.RawMessage.
Альберт

1
@albert, працює для мене: play.golang.org/p/XYsozrJrSl . Однак ви праві, що використання вказівника було б кращим. Інверсія мого коду працює некоректно: play.golang.org/p/46JOdjPpVI . За допомогою вказівника це виправлено: play.golang.org/p/ZGwhXkYUT3 .
Стівен Вайнберг,

3
Після того, як ви оновили * json.RawMessage, вам тепер потрібно розподілити їх у викликах json.Unmarshal.
Kyle Lemons

2

Крім відповіді Стівена Вайнберга, я з тих пір застосував зручний інструмент під назвою 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

1

Ось елегантний спосіб зробити подібне. Але чому JSON частково не є маршалом? Це не має сенсу.

  1. Створіть свої структури для чату.
  2. Розшифруйте json до структури.
  3. Тепер ви можете легко отримати доступ до всього в Struct / Object.

Подивіться нижче на робочий код. Скопіюйте та вставте його.

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 із сервера, оновити окремі поля та опублікувати його назад на сервері.
Артем Баскаков
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.