Skip to content

json.Unmarshal not work good at object #48959

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
coco-roll opened this issue Oct 14, 2021 · 9 comments
Closed

json.Unmarshal not work good at object #48959

coco-roll opened this issue Oct 14, 2021 · 9 comments

Comments

@coco-roll
Copy link

coco-roll commented Oct 14, 2021

What version of Go are you using (go version)?

$ go version
go version go1.16.4 darwin/amd64

Does this issue reproduce with the latest release?

The lastst release is also reproduce

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/Library/Caches/go-build"
GOENV="/Users/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/go"
GOPRIVATE=""
GOPROXY="direct"
GOROOT="/usr/local/Cellar/go/1.16.4/libexec"
GOSUMDB=""
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.16.4/libexec/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.16.4"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/go/src/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/7p/syywyq8s4t500nf_fhcw58km0000gn/T/go-build1825247537=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	var testMap map[string]interface{}

	strArr := "{\"11911007\":{\"11911007\":1,\"119110010\":1,\"11911006\":1,\"11911005\":1,\"119110011\":1,\"119110012\":1,\"11911004\":1,\"119110015\":1},\"119110010\":{\"000000\":1}}"
	if err := json.Unmarshal([]byte(strArr), &testMap); err != nil {
		panic(err)
	}
	fmt.Printf("first time Unmarshal map is:%+v\n", testMap)

	if err := json.Unmarshal([]byte("{\"11911007\":{\"000001\":1}}"), &testMap); err != nil {
		panic(err)
	}
	fmt.Printf("second time Unmarshal map is:%+v\n", testMap)

	return
}

What did you expect to see?

See:

first time Unmarshal map is:map[119110010:map[000000:1] 11911007:map[119110010:1 119110011:1 119110012:1 119110015:1 11911004:1 11911005:1 11911006:1 11911007:1]]
second time Unmarshal map is:map[1911007:map[000001:1]]

What did you see instead?

See:

first time Unmarshal map is:map[119110010:map[000000:1] 11911007:map[119110010:1 119110011:1 119110012:1 119110015:1 11911004:1 11911005:1 11911006:1 11911007:1]]
second time Unmarshal map is:map[119110010:map[000000:1] 11911007:map[000001:1]]
@dsnet
Copy link
Member

dsnet commented Oct 14, 2021

This is working as expected. Unmarshal generally uses merge semantics (although somewhat inconsistently) when unmarshaling JSON objects.

If you want the result you are expecting, then I suggest setting testMap = nil before the second json.Unmarshal call.

@mvdan
Copy link
Member

mvdan commented Oct 14, 2021

What @dsnet said; this is pretty clearly documented at https://pkg.go.dev/encoding/json. If you think the docs aren't clear enough, we'd be happy to hear suggestions or feedback.

@mvdan mvdan closed this as completed Oct 14, 2021
@coco-roll
Copy link
Author

@dsnet @mvdan why not clear before unmarshaling map as Slice do? This is clear to the user。
Or explain the design?

@mvdan
Copy link
Member

mvdan commented Nov 2, 2021

Even if we wanted to do that, we can't make the change to the existing API as it would break users.

@coco-roll
Copy link
Author

@mvdan This is a problem.
Can add a option to decide whether to clear?

@dsnet
Copy link
Member

dsnet commented Nov 2, 2021

You're welcome to file a new issue with a concrete proposal of what this option would look like and how it would work.

@coco-roll
Copy link
Author

coco-roll commented Nov 3, 2021

@dsnet I'd like to add a flag in :

// IsCLearMap  When this flag is true to unmarshal
//a JSON object into a map allocates a new map whatever.
//Otherwise Unmarshal reuses the existing map,
//keeping existing entries
var IsCLearMap = false
      ......
if kv.IsValid() {
               //use the flag
if i == 0 && IsCLearMap {
	v.Set(reflect.MakeMap(t))
}
v.SetMapIndex(kv, subv)
i++
}
     ......

Then can use

json.IsCLearMap = true

to decide clear when unmarshal a JSON object to a map

I make a test,it look work good.
default:


import (
	"encoding/json"
	"fmt"
)

func main() {
	var testMap map[string]interface{}
	//json.IsCLearMap = true
	strArr := "{\"11911007\":{\"11911007\":1,\"119110010\":1,\"11911006\":1,\"11911005\":1,\"119110011\":1,\"119110012\":1,\"11911004\":1,\"119110015\":1},\"119110010\":{\"000000\":1}}"
	if err := json.Unmarshal([]byte(strArr), &testMap); err != nil {
		panic(err)
	}
	fmt.Printf("first time Unmarshal map is:%+v\n", testMap)

	if err := json.Unmarshal([]byte("{\"11911007\":{\"000001\":\"string\"}}"), &testMap); err != nil {
		panic(err)
	}
	fmt.Printf("second time Unmarshal map is:%+v\n", testMap)

	return
}

output is:

first time Unmarshal map is:map[119110010:map[000000:1] 11911007:map[119110010:1 119110011:1 119110012:1 119110015:1 11911004:1 11911005:1 11911006:1 11911007:1]]
second time Unmarshal map is:map[11911007:map[000001:string]]

import (
	"encoding/json"
	"fmt"
)

func main() {
	var testMap map[string]interface{}
	json.IsCLearMap = true
	strArr := "{\"11911007\":{\"11911007\":1,\"119110010\":1,\"11911006\":1,\"11911005\":1,\"119110011\":1,\"119110012\":1,\"11911004\":1,\"119110015\":1},\"119110010\":{\"000000\":1}}"
	if err := json.Unmarshal([]byte(strArr), &testMap); err != nil {
		panic(err)
	}
	fmt.Printf("first time Unmarshal map is:%+v\n", testMap)

	if err := json.Unmarshal([]byte("{\"11911007\":{\"000001\":\"string\"}}"), &testMap); err != nil {
		panic(err)
	}
	fmt.Printf("second time Unmarshal map is:%+v\n", testMap)

	return
}

output is:

first time Unmarshal map is:map[119110010:map[000000:1] 11911007:map[119110010:1 119110011:1 119110012:1 119110015:1 11911004:1 11911005:1 11911006:1 11911007:1]]
second time Unmarshal map is:map[11911007:map[000001:string]]

@ianlancetaylor
Copy link
Contributor

@coco-roll Please file suggestions in a new issue, not this closed issue. Thanks. See https://golang.org/s/proposal.

That said we are not going to add a global variable like IsClearMap. That would be racy, and it would be break programs that have multiple goroutines using JSON simultaneously if they don't all want the same behavior. We never use global variables in that way in the standard library. Some other approach would be required.

@coco-roll
Copy link
Author

@ianlancetaylor Ok, i open a new issue at #49334. We can discuss in there.

@golang golang locked and limited conversation to collaborators Nov 4, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants