From 6e8d3a8c8d0247091e7db7883788bcf66095d35a Mon Sep 17 00:00:00 2001 From: Vic Luo Date: Sun, 25 Jan 2026 14:16:06 -0500 Subject: [PATCH] Fix: Boolean values incorrectly parsed as strings in struct unmarshaling --- decode.go | 5 +++-- hjson_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/decode.go b/decode.go index 06b0a16..98a7a2e 100644 --- a/decode.go +++ b/decode.go @@ -701,6 +701,7 @@ func (p *hjsonParser) readObject( var newDest reflect.Value var newDestType reflect.Type + currentElemType := elemType if stm != nil { sfi, ok := stm.getField(key) if ok { @@ -713,7 +714,7 @@ func (p *hjsonParser) readObject( return nil, p.errAt("Internal error") } newDestType = newDestType.Field(i).Type - elemType = newDestType + currentElemType = newDestType if newDest.IsValid() { if newDest.Kind() != reflect.Struct { @@ -732,7 +733,7 @@ func (p *hjsonParser) readObject( // duplicate keys overwrite the previous value var val interface{} - if val, err = p.readValue(newDest, elemType); err != nil { + if val, err = p.readValue(newDest, currentElemType); err != nil { return nil, err } if p.nodeDestination { diff --git a/hjson_test.go b/hjson_test.go index 4d0cee2..31d1e42 100644 --- a/hjson_test.go +++ b/hjson_test.go @@ -1793,3 +1793,54 @@ j: null, k: "another text", l: null t.Error("Should have failed, should not be possible to call pointer method UnmarshalText() on the map elements because they are not addressable.") } } + +type UnmarshalExtraFieldsConfig struct { + Type string `json:"type"` + Extra map[string]interface{} +} + +// We implement UnmarshalJSON to capture the extra fields +func (c *UnmarshalExtraFieldsConfig) UnmarshalJSON(data []byte) error { + type Alias UnmarshalExtraFieldsConfig + aux := &struct { + *Alias + }{ + Alias: (*Alias)(c), + } + // This will unmarshal the known fields + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + // Now unmarshal everything into a map to get extra fields + var raw map[string]interface{} + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + + if val, ok := raw["allow_local"]; ok { + if _, isBool := val.(bool); !isBool { + return fmt.Errorf("allow_local is not bool, got %T", val) + } + } else { + return fmt.Errorf("allow_local missing") + } + return nil +} + +func TestUnmarshalStructWithExtraFields(t *testing.T) { + txt := ` +{ + type: network_allow + allow_local: true +} +` + var cfg UnmarshalExtraFieldsConfig + err := Unmarshal([]byte(txt), &cfg) + if err != nil { + if strings.Contains(err.Error(), "allow_local is not bool") { + t.Fatalf("Bug reproduced: %v", err) + } + t.Fatalf("Unexpected error: %v", err) + } +}