Advanced example of Golang JSON

  • 2020-06-19 10:31:11
  • OfStack

Pain points

json is one of the most commonly used data transmission formats, plain text, easy to use, easy to read, and widely used in the communication process.

Have you ever been caught in the dilemma of json where a field fits into a certain type? (Example: an port field is defined and you don't know whether to fill in 8080 or "8080")

Have you ever had an json reverse parse error because the type of field you filled in didn't match? Such as:

[

json: cannot unmarshal number into Go struct field Host.port of type string

]

Do you have json's requirement that a field be compatible with two or more data structures ?

Do you want to make your program more elegant and adaptable, without getting bogged down in these little details?

If you have or want to get access you can check out this article.

A recurring problem

We gave the user 1 json as follows:


{
 "name":"yulibaozi",
 "port":8080
}

However, the business side mistakenly filled in "8080", which resulted in our program's reverse analysis and error, leading to the business failure.

[

json: cannot unmarshal number into Go struct field Host.port of type string

]

Maybe you think this is a business problem, but I think we can solve it more elegantly.

How to solve the problem

We first defined a structure


type Host struct {
 Name string `json:"name"`
 Port Port `json:"port"`
}

If you think carefully, Port is neither int nor string, but Port, while Port is:


type Type int

const (
 Int Type = iota
 String
)

type Port struct {
 Type Type
 IntVal int
 StrVal string
}

In Port structure, we found Type type, while Type type includes int and string. The next important step is to implement the following two interfaces.


json.Unmarshaller interface
json.Marshaller interface

The implementation code is as follows:


type Port struct {
 Type Type
 IntVal int
 StrVal string
}

//  implementation  json.Unmarshaller  interface 
func (port *Port) UnmarshalJSON(value []byte) error {
 if value[0] == '"' {
  port.Type = String
  return json.Unmarshal(value, &port.StrVal)
 }
 port.Type = Int
 return json.Unmarshal(value, &port.IntVal)
}

//  implementation  json.Marshaller  interface 
func (port Port) MarshalJSON() ([]byte, error) {
 switch port.Type {
 case Int:
  return json.Marshal(port.IntVal)
 case String:
  return json.Marshal(port.StrVal)
 default:
  return []byte{}, fmt.Errorf("impossible Port.Type")
 }
}

Next test:

Test inverse resolution

Test the reverse parsing int

Data of json are given:


{"name":"yulibaozi","port":8090}

The structural data obtained by inverse analysis are as follows:


&{Name:yulibaozi Port:{Type:0 IntVal:8090 StrVal:}}

Test reverse parsing string:

Data of json are given:


{"name":"yulibaozi","port":"8090"}

The structural data obtained by inverse analysis are as follows:


&{Name:yulibaozi Port:{Type:1 IntVal:0 StrVal:8090}}

Test the encoded json

The structure of test code int is as follows:


host := &Host{
   Name: "yulibaozi",
   Port: Port{
     Type:  Int,
     IntVal: 8080,
   },
 }

The encoded json is as follows:


type Host struct {
 Name string `json:"name"`
 Port Port `json:"port"`
}
0

The structure of test code string is as follows:


type Host struct {
 Name string `json:"name"`
 Port Port `json:"port"`
}
1

The encoded json data is as follows:


type Host struct {
 Name string `json:"name"`
 Port Port `json:"port"`
}
2

In the anti-coding test, you will see that when json fills in different types, it encodes the corresponding fields in the structure.

In the coding test, the specific encoded data is determined by Type.

conclusion

Actually, this article is to share the next json used in the tip, he broke when using json, need the mechanical impression of data structure, turned to the changeable, flexible style, in fact, it is the core of this little tips realize Unmarshaller Marshaller these two structures, their implementation is the key to this share, of course, you can achieve such as opening, json a field compatible with two and above structure, of course, You can play with yaml,toml, you get the answer you want.


Related articles: