3

I have an app that receives messages which are JSON. The JSON has various "sections" (example below). Each section has a name, but the structure beyond that is completely different per section.

What I want to do is go through the sections and for each, Unmarshal into the appropriate object. But strangely I've found this difficult, as it seems you can either Unmarshal the entire JSON into an object, or you can get a generic map[string]interface{}. All the example code I've found gets into type switching and manually assigning variables...I was hoping to do the neato Unmarshal directly into an object.

Is there a way to feed Unmarshal a subset of the JSON? I could slice and dice the byte[] myself but that seems ghastly...surely others have experienced something like this?

Here is what I've played with.

package main

import "encoding/json"

type Book struct {
    Author string
    Title  string
    Price  float64
}

type Movie struct {
    Title  string
    Year   float64
    Stars  float64
    Format string
}

var sections map[string]interface{}

func main() {

    /*
     * "Book" and "Movie" are "sections".
     * There are dozens of possible section types,
     * and which are present is not known ahead of time
     */

    incoming_msg_string := `
{
    "Book" : {
        "Author" : "Jack Kerouac",
        "Title" : "On the Road",
        "Price" : 5.99
    }, 
    "Movie" : {
        "Title" : "Sherlock Holmes vs. the Secret Weapon",
        "Year" : 1940,
        "Stars" : 2.5,
        "Format" : "DVD"
    }
}`

    /*
     * this code gets me a list of sections
     */

    var f interface{}
    err := json.Unmarshal([]byte(incoming_msg_string), &f)
    if err != nil {
        panic(err)
    }

    var list_of_sections []string
    for section_type, _ := range f.(map[string]interface{}) {
        list_of_sections = append(list_of_sections, section_type)
    }

    /*
       * next I would like to take the JSON in the "book" section
       * and unmarshal it into a Book object, then take the JSON
       * in the "movie" section and unmarshal it into a Movie object,
       * etc.
       *
       * https://blog.golang.org/json-and-go has an example on
       * decoding arbitrary data, but there's only one Unmarshaling.
       *
       * json.RawMessage has an example in the docs but it assumes
       * the objects are the same type (I think).  My attempts to use
       * it with a two-field struct (section name, and a raw message)
       * gave runtime errors.  Likewise unmarshaling into a 
       * []json.RawMessage gave "panic: json: cannot unmarshal object into Go value of type []json.RawMessage"
       *
       * What I'm looking for is something like:
       *   json.Unmarshal(some_json["a certain section"],&object)
       *
    */
}

Any breadcrumb trail hints much appreciated.

1 Answer 1

13

Do your initial unmarshal to a type Sections map[string]json.RawMessage variable. You will then have the section type and the raw data associated with it. You can either switch on the section type and unmarshal to the specific section struct or unmarshal to a map[string]interface{} and handle them generically. (Whatever works best for your app.)

2
  • Thank you - that is beautiful. I don't know why several hours of googling and experimenting didn't uncover that simplicity, but it was a night of learning :-) Thanks much.
    – raindog308
    Commented Jul 24, 2016 at 15:19
  • Wow. After a few hours of searching it turns out to be THAT easy. Thx
    – jannis
    Commented Dec 31, 2021 at 18:05

Not the answer you're looking for? Browse other questions tagged or ask your own question.