Write in front
When writing Go, serialization and deserialization are often used to record the pitfalls encountered.
The null pointer will be parsed into the string "null"
type Person struct { Name string Age int } func main() { var p *Person bytes, err := (p) checkError(err) ("len:%d, result:%s\n", len(bytes), string(bytes)) // len:4, result:null } func checkError(err error) { if err != nil { ("err:%+v\n", err) } }
When a null pointer is a "null" string, I thought it was a "" or an error was reported.
There is another strange pit
type Person struct { Name string Age int } func main() { var p *Person s := `null` err := ([]byte(s), &p) checkError(err) ("p:%+v\n", p) // p:<nil> }
This does not report an error, but gets a null pointer p
If you change s to other stringss := "abc"
, an error is reported:invalid character 'a' looking for beginning of value
What I understood before wasnull
For Go, it should followabc
There is no difference, it's all strings. I didn't expect that they are different. Let's dig into the underlying code.
Before UnMarshal it had acheckValid
function
func checkValid(data []byte, scan *scanner) error { () for _, c := range data { ++ if (scan, c) == scanError { return } } if () == scanError { return } return nil }
checkValid
The function will check every character and call the step function. The initial step value isstateBeginValue
// stateBeginValue is the state at the beginning of the input. func stateBeginValue(s *scanner, c byte) int { if isSpace(c) { return scanSkipSpace } switch c { case '{': = stateBeginStringOrEmpty return (c, parseObjectKey, scanBeginObject) case '[': = stateBeginValueOrEmpty return (c, parseArrayValue, scanBeginArray) case '"': = stateInString return scanBeginLiteral case '-': = stateNeg return scanBeginLiteral case '0': // beginning of 0.123 = state0 return scanBeginLiteral case 't': // beginning of true = stateT return scanBeginLiteral case 'f': // beginning of false = stateF return scanBeginLiteral case 'n': // beginning of null = stateN return scanBeginLiteral } if '1' <= c && c <= '9' { // beginning of 1234.5 = state1 return scanBeginLiteral } return (c, "looking for beginning of value") }
There is such a piece of code that deals with the first character and finds that it isn
There is special processing and the next character processing function is set to stateN
// stateN is the state after reading `n`. func stateN(s *scanner, c byte) int { if c == 'u' { = stateNu return scanContinue } return (c, "in literal null (expecting 'u')") }
That is, the next character must beu
, the next character processing function is stateNu
// stateNu is the state after reading `nu`. func stateNu(s *scanner, c byte) int { if c == 'l' { = stateNul return scanContinue } return (c, "in literal null (expecting 'l')") }
That is, the next character must bel
, the next character processing function is stateNul
// stateNul is the state after reading `nul`. func stateNul(s *scanner, c byte) int { if c == 'l' { = stateEndValue return scanContinue } return (c, "in literal null (expecting 'l')") }
That is, the next character must bel
, and the next character processing function is stateEndValue.
visiblecheckValid
Functions have special handling of true, false, etc. Pay attention when using it.
For functions, it is found through debugging that it also has special processing for null pointers.
type ptrEncoder struct { elemEnc encoderFunc } func (pe ptrEncoder) encode(e *encodeState, v , opts encOpts) { if () { ("null") return } if ++; > startDetectingCyclesAfter { // We're a large number of nested calls deep; // start checking if we've run into a pointer cycle. ptr := () if _, ok := [ptr]; ok { (&UnsupportedValueError{v, ("encountered a cycle via %s", ())}) } [ptr] = struct{}{} defer delete(, ptr) } (e, (), opts) -- }
If it is a null pointer, the string "null" will be returned and no error will be reported.
Int type will be parsed into float64
type Person struct { Name string Age int } func main() { p := &Person{ Name: "text", Age: 18, } bytes, err := (p) checkError(err) pMap := make(map[string]interface{}) err = (bytes, &pMap) checkError(err) for k, v := range pMap { ("k:%s,v:%+v, vtype:%v\n", k, v, (v)) } } func checkError(err error) { if err != nil { ("err:%+v\n", err) } }
result
k:Name,v:text, vtype:string
k:Age,v:18, vtype:float64
Apparently, the Age type becomes float64. What problems will be caused? When the int size exceeds 6 digits, it becomes a scientific counting method. For example, Age=1234567, the result is
k:Name,v:text, vtype:string
k:Age,v:1.234567e+06, vtype:float64
At this time, if you directly update the map to db, the field that was originally of type int becomes float, and an error will be reported
This is the article about the pitfalls and solutions encountered by Golang parsing JSON. For more relevant content on Golang parsing JSON, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!