Search code examples
jsongounmarshalling

Golang's json.Unmarshal does not explicitly set pointer value to nil if the field is not present in JSON


Consider this example:

package main

import (
    "encoding/json"
    "fmt"
)

type InternalStructFirst struct {
    Field string `json:"field"`
}
type ExternalStruct struct {
    StructA InternalStructFirst `json:"struct_a"`
    StructB *InternalStructFirst `json:"struct_b"`
}

func main() {
    var structVar ExternalStruct
    string1 := "{\"struct_a\":{\"field\":\"a\"},\"struct_b\":{\"field\":\"b\"}}"

    json.Unmarshal([]byte(string1), &structVar)
    fmt.Printf("first: %+v %+v\n", structVar.StructA, structVar.StructB)

    string2 := "{\"struct_a\":{\"field\":\"c\"}}"
    json.Unmarshal([]byte(string2), &structVar)
    fmt.Printf("second: %+v %+v\n", structVar.StructA, structVar.StructB)

    var structVar2 ExternalStruct
    json.Unmarshal([]byte(string2), &structVar)
    fmt.Printf("third: %+v %+v\n", structVar2.StructA, structVar2
}


Which outputs:

first: {Field:a} &{Field:b}
second: {Field:c} &{Field:b}
third: {Field:} <nil>

When I do json.Unmarshal the first time, StructB is presented in the response, and is unmarshalled correctly. But when I do it the second time, it seems like it doesn't explicitly set it to nil when the field is not presented in the response. It does set it to nil (or apparently rather does not set it to something) when doing it to a struct that does not have this field set (as seen in the third example).

How can I change my example so the second json.Unmarshal would explicitly set the StructB to nil when parsing a JSON string that does not have struct_b field in it, if I have struct_b set to not nil already?


Solution

  • You can't. json.Unmarshal will not modify struct fields that are not represented in the input JSON. Either use an empty struct to start with, or set any fields to whatever value you want them to have if they're not in the JSON input before you call Unmarshal.