Частично преобразование JSON в карту в Go


99

Мой сервер websocket получит и демаршалирует данные 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)
}

Спасибо за любые предложения / помощь!

Ответы:


195

Это может быть выполнено путем демаршалинга в файл 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-символов и т.д.), поэтому я тоже ее демаршалирую.
ANisus

1
Я исправил свой ответ, чтобы он соответствовал твоему. Спасибо
Стивен Вайнберг

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 .
Стивен Вайнберг

3
После обновления до * json.RawMessage вам теперь нужно разыменовать их в вызовах json.Unmarshal.
Кайл Лемонс

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 в Struct.
  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.