10

I want to return a structure that looks like this:

{
    results: [
        ["ooid1", 2.0, "Söme text"],
        ["ooid2", 1.3, "Åther text"],
    ]
}

That's an array of arrags that is string, floating point number, unicode character.

If it was Python I'd be able to:

import json
json.dumps({'results': [["ooid1", 2.0, u"Söme text"], ...])

But in Go you can't have an array (or slice) of mixed types.

I thought of using a struct like this:

type Row struct {
    Ooid string
    Score float64
    Text rune
}

But I don't want each to become a dictionary, I want it to become an array of 3 elements each.

1
  • Why don't you want a struct? Just for JSON? An array of interfaces is annoying to deal with everywhere else in your code so I'd only use it if there was (almost) no other code accessing the data and if it was only for marshaling into JSON or something. You can easily write a custom marshaller that will write the struct you give it as a JSON array. Commented Apr 11, 2015 at 22:30

3 Answers 3

15

We can customize how an object is serialized by implementing the json.Marshaler interface. For our particular case, we seem to have a slice of Row elements that we want to encode as an array of heterogenous values. We can do so by defining a MarshalJSON function on our Row type, using an intermediate slice of interface{} to encode the mixed values.

This example demonstrates:

package main

import (
    "encoding/json"
    "fmt"
)

type Row struct {
    Ooid  string
    Score float64
    Text  string
}

func (r *Row) MarshalJSON() ([]byte, error) {
    arr := []interface{}{r.Ooid, r.Score, r.Text}
    return json.Marshal(arr)
}

func main() {
    rows := []Row{
        {"ooid1", 2.0, "Söme text"},
        {"ooid2", 1.3, "Åther text"},
    }
    marshalled, _ := json.Marshal(rows)
    fmt.Println(string(marshalled))
}

Of course, we also might want to go the other way around, from JSON bytes back to structs. So there's a similar json.Unmarshaler interface that we can use.

func (r *Row) UnmarshalJSON(bs []byte) error {
    arr := []interface{}{}
    json.Unmarshal(bs, &arr)
    // TODO: add error handling here.
    r.Ooid = arr[0].(string)
    r.Score = arr[1].(float64)
    r.Text = arr[2].(string)
    return nil
}

This uses a similar trick of first using an intermediate slice of interface{}, using the unmarshaler to place values into this generic container, and then plop the values back into our structure.

package main

import (
    "encoding/json"
    "fmt"
)

type Row struct {
    Ooid  string
    Score float64
    Text  string
}

func (r *Row) UnmarshalJSON(bs []byte) error {
    arr := []interface{}{}
    json.Unmarshal(bs, &arr)
    // TODO: add error handling here.
    r.Ooid = arr[0].(string)
    r.Score = arr[1].(float64)
    r.Text = arr[2].(string)
    return nil
}

func main() {
    rows := []Row{}
    text := `
    [
          ["ooid4", 3.1415, "pi"],
          ["ooid5", 2.7182, "euler"]
        ]
    `
    json.Unmarshal([]byte(text), &rows)
    fmt.Println(rows)
}

You can read a full example here.

Sign up to request clarification or add additional context in comments.

Comments

12

Use []interface{}

type Results struct {
     Rows []interface{} `json:"results"`
}

You will then have to use type assertion if you want to access the values stored in []interface{}

for _, row := range results.Rows {
    switch r := row.(type) {
    case string:
        fmt.Println("string", r)
    case float64:
        fmt.Println("float64", r)
    case int64:
        fmt.Println("int64", r)
    default:
        fmt.Println("not found")
    } 
}

Comments

0

Some clumsy, but you can

type result [][]interface{}
type results struct {
    Results result
}

Working example https://play.golang.org/p/IXAzZZ3Dg7

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.