Summary of go json conversion behavior

I tried various json behavior of go, so I will summarize it.

How to convert to json

First of all, how to convert to json. The easiest way to do this is to do something like this:

import "encoding/json"
//...
jsonout, err := json.Marshal(Target object)

This method is easy to understand, but in reality, it is often converted in the following form.

encoder := json.NewEncoder(io where json is written.Writer)
err := encoder.Encode(Target object)

With the above method, any interface of io.Writer can be used, so you can write to a file (os.File), write to an http response (http.ResponseWriter), or buffer (bufio.Writer). There is a high degree of freedom in writing to.

Basic output

Now let's look at the output of the basic struct. The output content of json in the case of the following ordinary structure is as follows.

func main() {
  type Sample struct {
    IDString string
  }
  st := Sample{IDString: "xxfff"}
  out, _ := json.Marshal(st)
  fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
  // Output:
  // {"IDString":"xxfff"}
}

It is an image that the structure is converted to json as it is. It's intuitive. However, considering the naming convention of json, it is subtle whether it is said to be practical. Many people think that json's key name is lower camel and is often a snake case. To do this, you need to change the key name. To change the key name, use the tag character. The case where the key name is changed is as follows.

func main() {
  type Sample struct {
    IDString string `json:"id_string"`
  }

  st := Sample{IDString: "xxfff"}
  out, _ := json.Marshal(st)
  fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
  // Output:
  // {"id_string":"xxfff"}
}

Many people find it troublesome to insert tag characters one by one. However, there are no nice options in the official json library, so everyone is doing their best by using Tools.

About json tag

The format of the json tag is as follows.

`(...) json:"[<key>][,<flag1>[,<flag2>]]" (...)`

As mentioned above, there are some json tags that can be specified other than changing the key name. The items that can be specified are as follows.

--Key name

Key name

As the name implies, this item is a json key name field. As will be described later, this is the highest priority among the key name specifications. If you specify -, that field will be skipped. If you write nothing and only ,, the name of the field of the structure is used as usual.

omitempty

If this item is specified, the value will be skipped when the value is zero. In the case of go language, the initial value is treated as zero value, so I think that this item is not used for types other than pointer type. The image is as follows.

func main() {
  type Sample struct {
    ID      string  `json:",omitempty"`
    Pointer *string `json:",omitempty"`
  }
  s := Sample{ID: "", Pointer: nil}
  out, _ := json.Marshal(s)
  fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
  // Output:
  // {}
}

string

This item is quite special personally and changes the value to string type. The following built-in types are supported.

Also, since the string type is also converted to the string type, " is escaped and output.

func main() {
  type Sample struct {
    ID string `json:",string"`
  }
  s := Sample{ID: "xxffid"}
  out, _ := json.Marshal(s)
  fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
  // Output:
  // {"ID":"\"xxffid\""}
}

Output order

The output order is similar to the order of the fields in the structure. However, things like map that are not in a fixed order are sorted alphabetically by key name. I wondered why this was the order, but considering the ease of testing, I feel like that.

func main() {
	s := map[string]string{
		"cup":    "one",
		"apple":  "two",
		"banana": "three",
	}
	out, _ := json.Marshal(s)
	fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
	// Output:
	// {"apple":"two","banana":"three","cup":"one"}
}

Embedded type

Embedded types are possible in go language structures. Since this type does not specify a field name, there is no key name. Therefore, basically, the fields of the embedded structure are expanded and output.

func main() {
  type Emb struct {
    Content string
  }
  type Sample struct {
    Emb
  }
  s := Sample{Emb: Emb{Content: "string"}}
  out, _ := json.Marshal(s)
  fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
  // Output: {"Content":"string"}
}

Of course there are exceptions, as I said basic. In the go language, it is possible to declare a type for an array. In this case, the expansion will result in the array being expanded, so you don't know the name of the key. Therefore, the type name is the key name, except for the embedded type of the array.

func main() {
  type Emb []string
  type Sample struct {
    Emb
  }
  s := Sample{Emb: Emb{"content1", "content2"}}
  out, _ := json.Marshal(s)
  fmt.Println(string(out)) // []Since it is a byte type, it is converted to a string
  // Output:
  // {"Emb":["content1","content2"]}
}

Key name priority

From the explanation so far, you can see that the key name is determined by various factors. However, that would result in a key collision. Therefore, go's json conversion has a priority. The priorities are shown below.

  1. Key name specified by tag
  2. Normal field name
  3. Embedded (the same priority applies to embedded fields)

If there are multiple items with the same priority, no key value will be output and no error will be output. It is a behavior that seems to fit if you are not careful when deploying the embedded type.

Promotion

There are various behaviors, but I think that many people find it bothersome to remember all of them and think about them one by one. So I created a tool to find out what json the struct outputs. https://github.com/komem3/stout

To use it, just specify the file path and structure name as shown below.

stout -path ./define.go SimpleStruct

I wrote it for this promotion.

References

Recommended Posts

Summary of go json conversion behavior
A Tour of Go Learning Summary
Summary of snippets when developing with Go
Behavior of multiprocessing.pool.Pool.map
JSON conversion class
Batch conversion of Excel files to JSON [Python]
Numerical summary of data
Summary of Tensorflow / Keras
Summary of pyenv usage
Features of Go language
Summary of Python arguments
Summary of logrotate software logrotate
Summary of test method
Summary of python file operations
Summary of Python3 list operations
2017.3.6 ~ 3.12 Summary of what we did
behavior of matplotlib: histogram normed
Convenient usage summary of Flask
Summary of Linux distribution types
Basic usage of Pandas Summary
Behavior of pandas rolling () method
A brief summary of Linux
Summary of Proxy connection settings