12

Is there a simple way in Go to check whether given JSON is either an Object {} or array []?

The first thing that comes to mind is to json.Unmarshal() into an interface, and then see if it becomes a map, or a slice of maps. But that seems quite inefficient.

Could I just check if the first byte is a { or a [? Or is there a better way of doing this that already exists.

0

3 Answers 3

15

Use the following to detect if JSON text in the []byte value data is an array or object:

 // Get slice of data with optional leading whitespace removed.
 // See RFC 7159, Section 2 for the definition of JSON whitespace.
 x := bytes.TrimLeft(data, " \t\r\n")

 isArray := len(x) > 0 && x[0] == '['
 isObject := len(x) > 0 && x[0] == '{'

This snippet of code handles optional leading whitespace and is more efficient than unmarshalling the entire value.

Because the top-level value in JSON can also be a number, string, boolean or nil, it's possible that isArray and isObject both evaluate to false. The values isArray and isObject can also evaluate to false when the JSON is invalid.

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

1 Comment

Just what I was looking for, thanks. I shouldn't have to worry about the top level value being anything else in my usecase, as they would all be invalid anyways.
9

Use a type switch to determine the type. This is similar to Xay's answer, but simpler:

var v interface{}
if err := json.Unmarshal(data, &v); err != nil {
    // handle error
}
switch v := v.(type) {
case []interface{}:
    // it's an array
case map[string]interface{}:
    // it's an object
default:
    // it's something else
}

4 Comments

I feel this is the best answer since the json is validated and it's simple and concise.
This is very inefficient.
That's what I thought about doing first, but it definitely is less efficient, especially given the use of reflection inside Unmarshal.
@robbieperry22 It is inefficient, but Unmarshal does not use reflection when unmarhsalling to interface{}.
6

Do step-by-step parsing of your JSON, using json.Decoder. This has the advantage over the other answers of:

  1. Being more efficient than decoding the entire value
  2. Using the official JSON parsing rules, and generating standard errors if you get invalid input.

Note, this code isn't tested, but should be enough to give you the idea. It can also be easily expanded to check for numbers, booleans, or strings, if desired.

func jsonType(in io.Reader) (string, error) {
    dec := json.NewDecoder(in)
    // Get just the first valid JSON token from input
    t, err := dec.Token()
    if err != nil {
        return "", err
    }
    if d, ok := t.(json.Delim); ok {
        // The first token is a delimiter, so this is an array or an object
        switch (d) {
        case '[':
            return "array", nil
        case '{':
            return "object", nil
        default: // ] or }, shouldn't be possible
            return "", errors.New("Unexpected delimiter")
        }
    }
    return "", errors.New("Input does not represent a JSON object or array")
}

Note that this consumed the first few bytes of in. It is an exercise for the reader to make a copy, if necessary. If you're trying to read from a byte slice ([]byte), convert it to a reader first:

t, err := jsonType(bytes.NewReader(myValue))

Go playground

2 Comments

I think the default: cannot be ] or } because the dec.Token() doesn't allow them. Does it?
@starriet: The default case should never trigger, that's true. It's just there as a safeguard. There are only 4 possible values for a json.Delim (pkg.go.dev/encoding/json#Delim). The two we expect are handled, the default case is only there so that we don't silently ignore an error, if it would ever happen. One could just delete the default case entirely, if desired. And I probably would in production code.

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.