Декодирование общих объектов JSON в один из многих форматов

#json #go

#json #Вперед #отмена сопоставления

Вопрос:

Я работаю над общим протоколом передачи сообщений на основе JSON в Go. Что я хотел бы сделать BaseMessage , так это иметь файл, содержащий общую информацию, такую как Type , timestamp , и т.д. Но в то же время я хочу иметь возможность определять более конкретные структуры сообщений для определенных типов данных.

Например:

 type Message struct {
    Type      string `json:type`
    Timestamp string `json:timestamp`

}

type EventMessage struct {
    Message
    EventType string
    EventCreator string
    EventData interface{}
}
 

У меня есть набор обработчиков, и чтобы определить, какой обработчик должен обработать сообщение, я сначала декодирую JSON в общий Message тип, чтобы проверить Type поле. Для этого примера я бы получил обработчик, связанный с типом сообщения «Событие».

Я сталкиваюсь с проблемами, когда затем хочу утвердить EventMessage тип в структуре.

Следующий код очень приблизительный, но, надеюсь, он отображает мое общее представление о том, как я пытаюсь обрабатывать сообщения.

 type Handler func(msg Message) Message
handlers := make(map[string]Handler)

var msg Message
decoder.Decode(amp;msg)
handler := handlers[msg.Type]
handler(msg)
 

Я пытался использовать interface{} , но затем декодер JSON просто создает карту, на которой я затем не могу утверждать ни один тип. Я нашел обходные пути, которые делают это возможным, но это очень некрасиво, вероятно, неэффективно и, скорее всего, подвержено ошибкам. Я хотел бы, чтобы все было просто и понятно, чтобы этот код можно было легко поддерживать.

Существует ли метод обработки общих объектов JSON в Go, чтобы декодированный JSON мог быть одним из многих форматов struct?

Я также играл с идеей иметь более конкретную информацию в a Data interface{} в основной Message структуре, но затем я столкнулся с той же проблемой невозможности утверждать какие-либо типы в интерфейсе. Должен быть лучший способ обработки форматов JSON, который мне просто не хватает.

Ответ №1:

Один из способов справиться с этим — определить структуру для фиксированной части сообщения с помощью json.Поле RawMessage для записи вариантной части сообщения. Декодируйте json.RawMessage к типу, специфичному для варианта:

 type Message struct {
  Type      string `json:"type"`
  Timestamp string `json:"timestamp"`
  Data      json.RawMessage
}  

type Event struct {
   Type    string `json:"type"`
   Creator string `json:"creator"`
}


var m Message
if err := json.Unmarshal(data, amp;m); err != nil {
    log.Fatal(err)
}
switch m.Type {
case "event":
    var e Event
    if err := json.Unmarshal([]byte(m.Data), amp;e); err != nil {
        log.Fatal(err)
    }
    fmt.Println(m.Type, e.Type, e.Creator)
default:
    log.Fatal("bad message type")
}
 

пример игровой площадки

Комментарии:

1. Спасибо, это работает замечательно. Я не думал использовать RawMessage!