JSON Post array or single object, how to unmarshal to struct or array of structs

7,479 views
Skip to first unread message

Tobias Contreras

unread,
Aug 5, 2013, 4:47:44 PM8/5/13
to golan...@googlegroups.com
New to GO,

I am reading in a json from post, I tried desperately to try to use a dynamic map to interface to deal with the uncertainty of whether it is a json array or single object, to no luck, so trying it the "Right" way and building a struct of the expected element(s) but how do I know if to json.Unmarshal to a struct or an array of structs?
Or do I try unmarshal both and what doesn't err, keep?

func PostStatement(w http.ResponseWriter, r *http.Request){

body, err := ioutil.ReadAll(r.Body)
var statements []Statement //but what if json is a single statement
err = json.Unmarshal([]byte(body), &statements)
...

Ibrahim M. Ghazal

unread,
Aug 6, 2013, 2:10:03 AM8/6/13
to Tobias Contreras, golang-nuts
> the uncertainty of whether it is a json array or single object

How is the JSON generated? Do you have control over it? I think it
might be easier to make the JSON consistent.

Martin Schnabel

unread,
Aug 6, 2013, 4:26:56 AM8/6/13
to golan...@googlegroups.com
as Ibrahim said, it is better to use either array or struct consistently.
but if you have no control over that you can simply check whether the
first non-whitespace byte is a square bracket. like this:

http://play.golang.org/p/dD98vrWF7a

Vasiliy Lozovoy

unread,
Aug 6, 2013, 2:56:20 AM8/6/13
to golan...@googlegroups.com
You can unmarshal to interface{} value and get either a map or a slice
of maps: http://play.golang.org/p/vNQUH4zj9v

Oleku Konko

unread,
Aug 6, 2013, 9:47:21 AM8/6/13
to golan...@googlegroups.com

I still strongly believe the way `go` handles json can be improved raised a question on this already :

Dustin Sallings

unread,
Aug 6, 2013, 1:22:54 PM8/6/13
to golan...@googlegroups.com
Tobias Contreras <tobi...@gmail.com>
writes:

> body, err := ioutil.ReadAll(r.Body)

If you'll please forgive me, I'm on a bit of a crusade against
ioutil.ReadAll -- I see it used all over the place unnecessarily and
often in harmful ways.

It requires a contiguous allocation of memory to hold whatever happens
to be on the other end of that io.Reader.

In *most* cases where I see people doing this, they want to do
something incremental with it when they're done (such as save it to a
file or run it through a parser).

e.g. here, you are just trying to parse JSON, which you can do just fine
with an io.Reader.

> var statements []Statement //but what if json is a single statement
> err = json.Unmarshal([]byte(body), &statements)

Instead of this, you can just use a json.Decoder and parse on the fly:

d := json.NewDecoder(r.Body)
err = d.Decode(&statements)

The same pattern works the same way with an infinite multi-object JSON
stream or a single small object.


In your particular case, it may not make a practical difference, but,
IMO, it's good to think in terms of Readers and Writers vs. large slabs
of contiguous memory.

--
dustin

Tobias Contreras

unread,
Aug 7, 2013, 3:54:04 PM8/7/13
to Ibrahim M. Ghazal, golang-nuts

Unfortunately I don't, I tried but no luck.
Should I try json decoding to array of structs, if fails, then bind to single struct or parse the body as string, check first character if not '[' then wrap string in brackets and pass through to json decoder to array of structs

DisposaBoy

unread,
Aug 8, 2013, 2:25:42 AM8/8/13
to golan...@googlegroups.com
What did you try and in what way did you fail

Tobias Contreras

unread,
Aug 13, 2013, 9:39:43 AM8/13/13
to golan...@googlegroups.com
I tried something like, but the buffer remains at EOF

decoder := json.NewDecoder(r.Body)

var statements []Statement
err := decoder.Decode(&statements)
if err != nil {
 var statement Statement
         err2 := decoder.Decode(&statement)
         if err2 != nil {
              //but err2 just errors with EOF, and I can't find a way to re-read body, or reset buffer
              return
         }
}

I also tried reading it into an interface{} but then I couldn't figure how to convert to the statement object or array

So last but not least going to try checking for first char square bracket

Tobias Contreras

unread,
Aug 13, 2013, 9:44:45 AM8/13/13
to golan...@googlegroups.com
That's really good to know, do you have a golang blog?

Tobias Contreras

unread,
Aug 13, 2013, 9:46:00 AM8/13/13
to golan...@googlegroups.com
Thank you, that was my other choice on how to deal with this, thank you for this code, I wasn't sure how to go about this yet.

DisposaBoy

unread,
Aug 13, 2013, 1:07:43 PM8/13/13
to golan...@googlegroups.com
I'm guessing the solution came from someone who replied to you directly as opposed to the list. This interface won't show me the quoted text... But if do you mind forwarding or quoting it if it isn't already quoted so other readers may see as well? thanks.

Martin Schnabel

unread,
Aug 13, 2013, 2:45:29 PM8/13/13
to golan...@googlegroups.com

Tobias Contreras

unread,
Aug 13, 2013, 5:34:40 PM8/13/13
to golan...@googlegroups.com
Thanks, thats the comment, it's a bummer there isn't a more elegant solution to trying to json decode to different structs until one fits without error
I'll be trying the character matching solution out tonight

On Tuesday, August 13, 2013 12:45:29 PM UTC-6, mb0 wrote:
https://groups.google.com/forum/#!msg/golang-nuts/rKVn8coJMlQ/QkIhuH65FD8J

Tobias Contreras

unread,
Aug 13, 2013, 7:27:13 PM8/13/13
to golan...@googlegroups.com
Hi, figured it out with mB0 help, here is some similar code I used, http://play.golang.org/p/-3nzhqzS1u

is there some way to re-read a reader/buffer instead of getting an EOF?

Tamás Gulácsi

unread,
Aug 14, 2013, 12:35:59 AM8/14/13
to golan...@googlegroups.com
Nope, you have to store the already read parts. This can be made as space efficient as possible by using an io.TeeReader with a bytes.Buffer and then an io.MultiReader, but if it fits in memory, than use ioutil.ReadAll and bytes.NewReader.
Reply all
Reply to author
Forward
0 new messages