Sunday, 7 September 2014

Loading an array/slice of objects from a JSON file in Golang

As usual, with a new project arriving my first question is "what can we learn on this one?"

So, having found that Go (aka Golang) is a decendant of Modula-2 and has some pretty nifty features, I decided that Golang was the way to Go (if you will pardon the expression).

The next thing that I do is head straight for the target, reading just enough to get me there. If the journey is a happy one I may flesh things out later, but when you are relying on the internet and googling, the search results are a bit like flicking through a book - but not as good because sometimes a book yields a gem that you weren't looking for.

The project is made of multiple servers (raspberry pi) doing crazy stuff all talking to each other as well as hardware and Arduinos. The first server was a sound player controlled by other remote servers. It loaded a name->filename map from a JSON file. Not too tricky.

The second one needed some routing tables loaded. I wanted pure object arrays loaded from JSON.

Note : You can't use an Array, you need a SLICE. Arrays are fixed size.

I Googled and came across lots of people on StackOverflow asking the same question.

A lot of the answers and questions revolved around interface{} which I tried and got working but it seemed a bit tricky because there are multiple type conversions involved - silly when all records are the same type. And there seemed to be NOBODY telling how to do it.

So, if you are trying to load an array slice of objects from a JSON file - bearing in mind I have been doing this for a couple of weeks with a lot of that time goofing off, here it is....

The data record I want to load

type command_record struct {
Source    string
What      string
Condition string
Value     string
Host      string
Action    string
}

Which we will represent in JSON as

[
    {
"Source": "sensor",
"What": "temp",
"Condition": "GT",
"Value": "35",
"Host": "sensor",
"Action": "REDON"
    },
    {
"Source": "host",
"What": "sensoralarm",
"Condition": "NIL",
"Value": "0",
"Host": "sensor",
"Action": "BLUOFF"
    }

 ]

So the slice is represented as 

var sensor_script *[]command_record

Note the * which means we are only declaring a pointer.

And we read the file into a slice of bytes

file, e := ioutil.ReadFile("./sensor.json")
if e != nil {
fmt.Printf("File error %v\n", e)
os.Exit(1)
}

Then we convert it objects using json.Unmarshal and print it just to show it works

json.Unmarshal(file, &sensor_script)
for _, v := range *sensor_script {
fmt.Println(v.Source)
fmt.Println(v.What)
fmt.Println(v.Condition)
fmt.Println(v.Value)
fmt.Println(v.Host)
fmt.Println(v.Action)
fmt.Println("=========")
}

And, after all that time - it is that simple. And I am sure that many many people have figured it out within ten minutes, but others have too google it and waste a lot of time. This is for them.

 The code and a sample JSON file can be found on Github at https://github.com/DaveAppleton/LoadObjectSliceFromJson