0

I'm new to Go and have been trying to figure out how to output the raw inner JSON "{\"data\":\"Some data"}" from "Outer". So far, no luck...

This is the input data:

{
 "Outer": "{\"data\":\"Some data\"}"
}

What I would like to get is the following as a string with the slashes:

{\"data\":\"Some data\"}
  • 1
    see http://stackoverflow.com/questions/21268000/unmarshaling-nested-json-objects-in-golang – Makpoc Apr 30 '15 at 08:50
  • possible duplicate of http://stackoverflow.com/questions/16674059/how-to-access-deeply-nested-json-keys-and-values – Dave C Apr 30 '15 at 16:25

2 Answers2

3

If you know the key ("Outer") you can do it like this (on the Playground):

package main

import (
    "encoding/json"
    "fmt"   
)

func main() {
    //Creating the maps for JSON
    m := map[string]json.RawMessage{}

    //Parsing/Unmarshalling JSON encoding/json
    err := json.Unmarshal([]byte(input), &m)

    if err != nil {
        panic(err)
    }
    fmt.Printf("%s", m["Outer"])
}

const input = `
{
 "Outer": "{\"data\":\"Some data\"}"
}
`

Note that your example json is missing the final escape after data. Without that you'll get an error.

If you don't know your structure see this reply for how to do it with arbitrary nested data.

Community
  • 1
  • 1
IamNaN
  • 6,654
  • 5
  • 31
  • 47
  • Your code is almost exactly what I need, however, it returns the data **without** the slashes. Is there a way to get it to keep the data exactly like `{\"data\":\"Some data\"} ` returned as a string? – Brandon Linux May 01 '15 at 08:33
  • Yes, 2 small changes will do that. See the edited answer. The map definition changes to use `json.RawMessage` and `fmt.Printf` is used to print the message. – IamNaN May 01 '15 at 09:10
  • Awesome! Thanks for all your help thus far! I have one more question for you. How would I go about storing `{\"data\":\"Some data\"}` in a variable (with the slashes included) as well? – Brandon Linux May 01 '15 at 17:08
  • Use [fmt.Sprintf](https://golang.org/pkg/fmt/#Sprintf) which returns a string. You can assign the result to a variable and it'll contain the slashes as well. – IamNaN May 01 '15 at 17:26
2

This started out as just a comment on a previous answer but grew.

Although you can unmarshal into a temporary map[string]interface{} (or map[string]json.RawMessage) that can do a lot of extra work if there are other "outer" fields that you just want to ignore and it also requires checking the type that was found (e.g. for the case of bad/unexpected input like {"Outer": 42}). That probably means adding something like this to the previous answer:

    str, ok := m["Outer"].(string)
    if !ok { return errors.New("…") }
    err := json.Unmarshal([]byte(str), &actual)

Much simpler is probably to use a temporary wrapping type like so:

    var tmp struct{ Outer string }
    err := json.Unmarshal(b, &tmp)
    if err != nil { … }

    var data actualType
    err := json.Unmarshal([]byte(tmp.Outer), &data)

You could either do this in a separate function that returns the type you wanted (or an error, e.g. func foo(b []byte) (actualType, error)). Or you could put a variation of this into a custom UnmarshalJSON method on a wrapper type so as to implement json.Unmarshaler. You could also do the reverse if you wanted to be able to marshal back into the same format.

Full runnable examples on the playground: https://play.golang.org/p/IXU2koyJ1A

Community
  • 1
  • 1
Dave C
  • 7,729
  • 4
  • 49
  • 65