Help in unmarshalling json

Hi,
I want to unmarshal this json:
{
“BSR Address”:“10.0.3.5”,
“228.0.0.0/8”:{
“10.0.3.5”:{
“Rp Address”:“10.0.3.5”,
“Rp HoldTime”:65535,
“Rp Priority”:1,
“Hash Val”:840583365
},
“Pending RP count”:100
}
}

But it doesnt work without json tags and these get generated at run time.

What have you tried so far?

Usually I use a map[string]T for objects with dynamic keys and map[string]interface{} if the values types are heterogeneous.

I need the key values and the struct values, but internal map[string]struct does not get unmarshaled properly.
I tried using the autogenerated struct given by json to go online converters too, but if i remove the json tags there it fails.

I do not see any structs. I just see a JSON object.

If you indeed have a struct you want to unmarshal to, you need to tag it. If you can’t rag, you have to unmarshal into a more dynamic type as mentioned above.

I’m currently on a mobile, therefore I can’t give you an example easily.

I tried with these structs:
type RP struct {
RpAddress string json:"Rp Address"
RpHoldTime int json:"Rp HoldTime"
RpPriority int json:"Rp Priority"
HashVal int json:"Hash Val"
}

type A struct {
Rpinfo map[string]RP
PendingRPCount int json:"Pending RP count"
}

type final struct {
Bsrinfo map[string]A
BSRAddress string json:"BSR Address"
}

Again, if you want to unmarshal into a struct, you have to specify all the fields. As you seemingly can’t, you have to use a more dynamic root type.


Alternatively there are ways to write more specific unmarshallers than the “default” implementations.

Such an implementation could store the known fields in the struct fields, and store the dynamic fields in a map that’s in another field of the struct.

I have not yet used such specialised unmarshallers, but I saw them in the wild.

Let’s assume you have input data with your json as a variable:

// Cleaned up JSON from your example
exampleJSON := []byte(`{
    "BSR Address": "10.0.3.5",
    "228.0.0.0/8": {
        "10.0.3.5": {
            "Rp Address": "10.0.3.5",
            "Rp HoldTime": 65535,
            "Rp Priority": 1,
            "Hash Val": 840583365
        },
        "Pending RP count": 100
    }
}`)

You can unmarshal that into a map[string]interface{} just fine:

func main() {
	// Unmarshal to generic type
	var dat map[string]interface{}
	if err := json.Unmarshal(exampleJSON, &dat); err != nil {
		panic(err)
	}

	fmt.Println("Printing key value pairs")
	for k, v := range dat {
		fmt.Println(k, ":", v)
	}
}

OUTPUT:

Printing key value pairs
BSR Address : 10.0.3.5
228.0.0.0/8 : map[10.0.3.5:map[Hash Val:8.40583365e+08 Rp Address:10.0.3.5 Rp HoldTime:65535 Rp Priority:1] Pending RP count:100]

When you say “I need the key values and the struct values, but internal map[string]struct does not get unmarshaled properly.”, I believe what you mean is “I want to be able to access the child maps and values from my code”, which is a simple matter of type assertion. Let’s update the example above to call a function that uses reflection to assert types on child maps and print them:

func main() {
	// Unmarshal to generic type
	var dat map[string]interface{}
	if err := json.Unmarshal(exampleJSON, &dat); err != nil {
		panic(err)
	}
	fmt.Println("Being more clever and using reflection to print values")
	printValues("", dat)
}

func printValues(prefix string, values map[string]interface{}) {
	for k, v := range values {
		// If our value is itself a map, recursively print its values
		if reflect.ValueOf(v).Kind() == reflect.Map {
			fmt.Println(prefix, k, ":")
			printValues(prefix+"\t", v.(map[string]interface{}))
		} else {
			// Otherwise, just print the key/value pair along with prefix
			fmt.Println(prefix, k, ":", v)
		}
	}
}

This will get you the following:

 Being more clever and using reflection to print values
 BSR Address : 10.0.3.5
 228.0.0.0/8 :
	 10.0.3.5 :
		 Rp Address : 10.0.3.5
		 Rp HoldTime : 65535
		 Rp Priority : 1
		 Hash Val : 8.40583365e+08
	 Pending RP count : 100

Here’s a go playground link I created for you where you can run this for yourself:

https://go.dev/play/p/1wqxTok7TIP

Try changing the hard-coded json and see what happens. Also be sure to check this excellent StackOverflow question and associated answers for other approaches. If that doesn’t get you where you need to go, I think you’re going to have to provide more example code and what you’re trying to accomplish.

Oh and one more very helpful resource I forgot to mention that can save you time: try pasting your JSON in to JSON-to-Go. That is always a great first troubleshooting step when dealing with random JSON from the internet. :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.