Repository: go
Issue: proposal: encoding/json: add a function to report whether a []byte represents a JSON array before unmarshal
Labels: Proposal

## Problem statement
Many web applications utilize JSON encoding as its primary data exchange format. Among these, some applications may even accept different JSON schemes as an acceptable argument for a single method. For an example, a common use case would be to accept both of the following JSONs as request bodies on a HTTP API endpoint:
```json
{
  "name": "foo"
}
```
```json
[
  {
    "name": "foo"
  },
  {
    "name": "bar"
  }
]
```
However, implementing this kind of behavior on the current state of the library is not trivial, and may lead to inefficient or even wrong implementations. There may exists multiple other ways to do this, but I will mostly focus on two:

1. **Unmarshal the received JSON into an ```interface{}```, and implement a common type assertion on a switch statement to decide which value you should convert the unmarshaled value**
    ```go
    var v interface{}
    json.Unmarshal(raw, &v) // ignoring error handling
    switch v.(type) {
    case []interface{}:
        // deal as a JSON array, possibly returning a slice of a desired type A
    
    case map[string]interface{}:
        // deal as a JSON object, returning an initialized struct of type A
    }
    ```

2. **Implement an in-place verification over the []byte to check if it starts with { or [**
    ```go
    // source: https://stackoverflow.com/a/55014220
    x := bytes.TrimLeft(data, " \t\r\n")

    isArray := len(x) > 0 && x[0] == '['
    isObject := len(x) > 0 && x[0] == '{'
    ```

Even though 1 utilizes native features from Go, it may be considered far from optimal due to the underlying reflection usage and the unmarshal itself. Considering ```raw``` the informed ```[]byte``` as a valid JSON, there's no need to unmarshal it entirely only to detect whether it represents a list or not. As for the other option, although being faster than 1, it may be considered error prone and unsafe for a lot of developers to deploy it on a production environment. I like its simplicity, but I guess it could be even more simple.

## Proposal
As a solution for this, I propose the implementation of a new exported function on pkg ```encoding/json``` to inform whether a raw ```[]byte``` represents a JSON array (i.e. is a valid JSON and starts with ```[```). The signature could be something as follows, similar to [Valid](https://github.com/golang/go/blob/085529bd5fa3ab508784f6a3d42f15d2dafddc65/src/encoding/json/scanner.go#L22):

```go
func IsArray(data []byte) bool
```

With this function available, one could easily implement the same use case described before as follows:
```go
// consider that raw is an already validated JSON
if json.IsArray(raw) {
  // unmarshal into a []A slice

} else {
  // unmarshal into a A struct
}  
```

As for its underlying implementation, I guess it could not only utilize the same [checkValid](https://github.com/golang/go/blob/085529bd5fa3ab508784f6a3d42f15d2dafddc65/src/encoding/json/scanner.go#L30) function to detect if ```data``` represents a valid JSON before searching for ```[```, but also utilize already implemented methods of the ```scanner``` struct as a reference for the JSON traversal. I'd love to contribute with a PR if you guys agree it's a valid addition to the library.

OBS: I'm still not sure about the name itself. I admit I don't feel very comfortable with the noun ```Array```, but it's the same noun utilized on the [JSON RFC](https://datatracker.ietf.org/doc/html/rfc7159#section-5). Should it be called slice? Any ideas?