Go Json-Xml
Go Json-Xml
com/chap-25-json-and-xml
• Marshal / Unmarshal
• Data stream
• JSON
• XML
• interface implementation
3 Introduction
JSON and XML are two formats that are used in modern applications. They are very convenient because they can be read and easily
understood by humans in comparison to other formats (like protocol buffers, for instance see [par:A-word-about])
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
4 Encoding / Decoding
Most of the time, applications transmit data to other applications, or to humans. In a Go program, the data that we manipulate is often in the
form of a type struct :
1 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
What if we want to transmit the data in the cart variable to another program? What if this program is not written in Go ? That’s why we
need encoding.
When transmitting a message through a communication channel, we want to make sure that the receiver will understand the message. Hence
the sender and the receiver will decide which encoding technique they will use. The term encoding technique might sound unfamiliar, but
you use the process of encoding every day... When we speak to another individual, we encode our thoughts into a coded form: the language
(English, French, French Canadian, Mandarin,...). When you speak, you encode your thoughts. (if you think before you speak, of course). The
language that we use has a set of rules that we have to respect to make our message understandable (syntax, grammar ...). This is the same
for all “coded form”.
• The “coded form” can be transformed back into the original message thanks to decoding.
In this section, we will study two “coded forms” : JSON and XML.
5 Marshal / Unmarshal
In the next section, you will see the term Marshal and Unmarshal appear. Marshaling and unmarshaling also denote a process.
• Marshaling is the process of transforming an object into a storable representation using a specific format.
“Marshaling” refers to the same concept as “encoding”. However, when you use this term, you explicitly say that you will work with precisely
defined objects (ex: a map, a slice, a variable of type struct...). An encoder operates on a stream of data, a marshaler operates on a data
structure, an object.
You might ask what a data stream is? It is a flow of data that arrives into a system. For instance, a system can receive data streams, flows of
data through a network connection. Handling a data stream can be a complex task because data can arrive in chunks, and it can arrive in an
unordered manner. Chunks may also vary in size, which can lead to memory issues.
6 What is JSON
JSON means JavaScript Object Notation. This is a text format that is used to encode/decode structured data. It has been specified first by
Douglas Crockford.
Here is an example :
2 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
[
{
"id": 1,
"first_name": "Annalise",
"last_name": "Marlin",
"city": "Bang Bon"
},
{
"id": 2,
"first_name": "Dacy",
"last_name": "Biffin",
"city": "Cuilco"
},
{
"id": 3,
"first_name": "Wendye",
"last_name": "Taillard",
"city": "Preobrazhenka"
}
]
• Each object starts with a curly brace and ends with a curly brace. ( {} )
"id": 3
7 Unmarshal JSON
To decode a string containing JSON, you will need to create type structs. Let’s take an example.
{
"cat": {
"name": "Joey",
"age": 8
}
}
We have an object with a property cat equal to another object with two properties: name and age . When you want to decode, you first
have to create a struct that will hold the data.
We have two fields, name and age, similar to the JSON structure that we want to decode. We can now launch the decoding function :
3 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
// json-xml/unmarshall-json/main.go
func main() {
myJson := []byte(`{"cat":{ "name":"Joey", "age":8}}`)
c := MyJson{}
err := json.Unmarshal(myJson, &c)
if err != nil {
panic(err)
}
fmt.Println(c.Cat.Name)
fmt.Println(c.Cat.Age)
}
We first need to convert the string to a slice of bytes. Then we create a new empty MyJson struct.
Then we call json.Unmarshal to set the values of our variable c . Note that json.Unmarshal arguments are :
Why do we need to pass a pointer? Because the Unmarshal function will modify the value c .
fmt.Println(c.Cat.Name)
fmt.Println(c.Cat.Age)
We expect to see :
Joey
8
But if you run the previous code, you will see that nothing is output! Why?
Because json.Unmarshal didn’t make a successful matching of the fields! Unmarshal will take the name of the fields of our struct and
try to find a corresponding property in the JSON string. If you look closely at our struct, we have capitalized the first letters to export the
fields.
7.0.0.1 Solutions
1. We can make our fields unexported (we remove the first capitalized letter). This is not a good idea. An external format to our
application should not interfere with our application types!
2. We need to get the ability to define other field names decorrelated from the field name in the JSON string.
// json-xml/unmarshall-json-tags/main.go
You see that we added here three tags to the fields of the type structs. Tags always have the same construction :
`json:"nameOfTheFieldInJson"`
The Unmarshaler will be able to find the fields in the JSON. They are accessible via the reflect package (more precisely the tag is a field
of the struct reflect.StructField )
4 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
8 Marshal JSON
To create a JSON string in go, you have to define a type struct that will define your data structure. We will take the example of an
e-commerce website that references products. Our objective is to create a JSON string from a slice of products.
We first define the Product type struct and the Category type struct (an article belong to a unique category) :
p := Product{ID: 42, Name: "Tea Pot", SKU: "TP12", Category: Category{ID: 2, Name: "Tea"}}
// json-xml/marshall-json/main.go
b, err := json.Marshal(p)
if err != nil {
panic(err)
}
fmt.Println(string(b))
The variable b is a slice of bytes. Note that the encoding operation might result in an error. We catch this error, and we make our program
panic1. Then we can print our result :
{"ID":42,"Name":"Tea Pot","SKU":"TP12","Price":30.5,"Category":{"ID":2,"Name":"Tea"}}
The formatting of the string is not optimal. We can use instead of another function to encode the JSON but also to indent it :
5 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
// json-xml/marshall-json/main.go
• the prefix string (every line of the output result will start with this string). In the example, this parameter is populated with an empty
string.
{
"ID": 42,
"Name": "Tea Pot",
"SKU": "TP12",
"Category": {
"ID": 2,
"Name": "Tea"
}
}
Usually, in JSON, the properties’ names do not begin with a capitalized letter. Here, the json package has simply taken the name of our
Product struct’s fields without any modification.
Tags can be added to the Product struct to control the printed result :
// json-xml/marshall-json/main.go
{
"id": 42,
"name": "Tea Pot",
"sku": "TP12",
"category": {
"id": 2,
"name": "Tea"
}
}
9 Struct tags
In the two previous sections we have seen that we can control the structure of the encoded JSON with tags. We have also learned how to
decode a JSON string and make our structure’s field name match the JSON properties’ name.
6 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
// json-xml/tags/main.go
func main() {
p := Product{ID: 42}
bI, err := json.MarshalIndent(p, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(bI))
}
{
"id": 42,
"name": ""
}
Here the field name is an empty string. We can add the omitempty directive into the struct tag to make it disappear from the outputed JSON
:
{
"id": 42
}
// json-xml/tags/main.go
Here we are telling the json package not to output the field Description in the encoded string.
7 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
// json-xml/access-tags/main.go
func main() {
p:= Product{ID:32}
t := reflect.TypeOf(p)
for i := 0; i < t.NumField(); i++ {
fmt.Printf("field Name : %s\n",t.Field(i).Name)
fmt.Printf("field Tag : %s\n",t.Field(i).Tag)
}
}
Here we first define the Product type that contains three fields with tags on each field. Then in the main function, we create a new variable
(of type Product ) named p . The type data is retrieved with the TypeOf function of the standard package reflect .
The function TypeOf returns an element of type Type ! The method NumField() will return the count of fields in the struct. In our case, it
will return 3.
We will then iterate over the fields of the struct with the help of the method Field() that takes the field index as argument. This last
function will return a variable of type StructField :
// package reflect
// file: type.go
type StructField struct {
Name string
PkgPath string
Type Type
Tag StructTag
Offset uintptr
Index []int
Anonymous bool
}
The type StructTag wraps a simple string. You have also access to conventional methods to manipulate the tags : Get()
//...
// for loop
fmt.Println(t.Field(i).Tag.Get("json"))
//...
id
name,omitempty
-
And now an example for the Lookup method. The purpose of the Lookup method is to find whether a tag is present or not :
// json-xml/access-tags/main.go
if tagValue, ok := t.Field(i).Tag.Lookup("test"); ok {
fmt.Println(tagValue)
} else {
fmt.Println("no tag 'test'")
}
8 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
A tag is organized as a list of key-value elements. In the figure 1 you can see that we have two elements. The first element has the json key
and the value name, omitempty.The second element has the key xml and its value is Name. Note that elements are separated by spaces.
If we follow this model, we can create the following tag with three keys.
<Product>
<id>42</id>
<name>Tea Pot</name>
<sku>TP12</sku>
<category>
<id>2</id>
<name>Tea</name>
</category>
</Product>
{
"id": 42,
"name": "Tea Pot",
"sku": "TP12",
"category": {
"id": 2,
"name": "Tea"
}
}
9 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
// json-xml/xml/decode/main.go
We added XML struct tags to the struct (il allow the decoder to know where to find the fields’ values).
// json-xml/xml/decode/main.go
func main() {
myXML := []byte(`<cat>
<name>Ti</name>
<age>23</age>
</cat>`)
c := MyXML{}
err := xml.Unmarshal(myXML, &c)
if err != nil {
panic(err)
}
fmt.Println(c.Cat.Name)
fmt.Println(c.Cat.Age)
}
Encoding XML is also very easy; you can use the xml.Marshal , or like here, the function xml.MarshalIndent to display a pretty xml string :
// json-xml/xml/encode/main.go
func main() {
p := Product{ID: 42, Name: "Tea Pot", SKU: "TP12", Price: 30.5, Category: Category{ID: 2, Name: "Tea"}}
bI, err := xml.MarshalIndent(p, "", " ")
if err != nil {
panic(err)
}
xmlWithHeader := xml.Header + string(bI)
fmt.Println(xmlWithHeader)
}
This header will give important information to the systems that will use your xml document. It gives information about the version of XML
used (“1.0” in our case that dates back to 1998) and the encoding of your document. The parser will need this information to decode your
document correctly.
The xml package defines a constant that you can use directly :
10 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
11 xml.Name type
If you create a variable of the following type
<MyXmlElement>
<name>Testing</name>
</MyXmlElement>
We can control the marshaling process of the property “name”, but we do not have control over the way the element MyXmlElement . For the
moment, if we want to change the name of this property to something else, we have to change the name of our type... Which is not very
convenient.
You can add a special field to your struct to control the name of the XML element :
The field has to be named XMLName ,the type must be xml.Name and in the tag, you can put the desired name of the element (that will be
injected during the marshaling process).
// json-xml/xml/xmlNameType/main.go
package main
import (
"encoding/xml"
"fmt"
)
func main() {
elem := MyXmlElement{Name: "Testing"}
m, err := xml.MarshalIndent(elem, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(m))
11 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
<myOwnName>
<name>Testing</name>
</myOwnName>
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
12.1 Attributes
You can add specific attributes to a field with XML. For instance :
<price currency="EUR">123</price>
Here the field is named price and there is an attribute named currency . You can use the following struct type to encode/decode it :
We have defined a struct named Price with two fields : Text that will contain the price (“123” in the example)
Currency that will contain the value of the currency attribute (“EUR” in the example) Note the tags used. To get the price, the tag is
`xml:",chardata"`
It tells Go to get the data contained between the tags opening and the closing price tag. To get attributes, you have to use the keyword
attr and the attribute name. The field is written as “character data” not has an xml element.
`xml:"currency,attr"`
12.2 CDATA
If you want to transfer XML via XML, you can use CDATA fields. CDATA fields allow you to inject characters like > and < into your XML
message. If you inject that kind of data without a CDATA field, your XML could become invalid.
Let’s take an example. We add to our XML a field myXml which is populated with character data. Note that character data is enclosed by
![CDATA[*and \lstinline!]]>!\textbf{.} The value of this field is therefore \lstinline! yo>12</yo>!
12 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
<product>
<price currency="EUR">123</price>
<myXml><![CDATA[<yo>12</yo>]]></myXml>
<name>Kit</name>
</product>
Here is a valid type struct that will allow you to retrieve the value of myXml :
// json-xml/xml/CDATA/main.go
Note that we created the type struct MyXml that has a single field Text . This single field has the following tag :
`xml:",cdata"`
12.3 Comments
Unlike JSON, you can add comments in your XML :
// json-xml/xml/comments/main.go
Here we added a field named Comment to our type struct Product . The special comment tag is added to the field.
// json-xml/xml/comments/main.go
c := Product{}
c.Comment = "this is my comment"
c.Price = Price{Text : "123",Currency:"EUR"}
c.Name = "testing"
<Product>
<!--this is my comment-->
<price currency="EUR">123</price>
<name>testing</name>
</Product>
13 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
// json-xml/xml/nesting/main.go
package main
import (
"encoding/xml"
"fmt"
)
func main() {
c := Product{}
c.Name = "testing"
b, err := xml.MarshalIndent(c, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
<Product>
<first>
<second>
<third>testing</third>
</second>
</first>
</Product>
You can see that the value of the field Name ( "testing" ) is enclosed into the element "third" which is also enclosed into the element
"second" and "first"
`xml:"first>second>third"`
• For JSON :
◦ Simply copy your JSON, paste it and let the tool generate the corresponding struct(s).
• For XML :
◦ https://github.com/miku/zek
◦ This is a CLI tool that allows you to generate structs based on sample XML
14 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
[
{
"id": 1,
"first_name": "Hedvig",
"last_name": "Largen",
"created_at": "2020-07-02T08:41:35Z"
},
{
"id": 2,
"first_name": "Win",
"last_name": "Alloisi",
"created_at": "2020-02-21T12:36:41Z"
}
]
• In this JSON, we have a collection of persons. Each person has a created_date. We want to Unmarshal this data to the variable users (a
slice of User ):
• In other words, the type has a method named UnmarshalJSON with the signature ([]byte) error
◦ If there is a “null” in the JSON string for the property "created_at" then the value of the field CreatedAt will be the zero value
of time.Time, which is January 1, year 1, 00:00:00.000000000 UTC.
• Then the method will attempt to parse the value stored in data :
◦ This line will parse the time with the format "2006-01-02T15:04:05Z07:00" ( RFC3339 is a constant of the package)
◦ Note that the enclosing double quotes are enclosing the parsing format.
15 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
◦ That’s because the value in JSON has the format "2020-07-02T08:41:35Z" and not 2020-07-02T08:41:35Z !
◦ We use the dereference operator * to set the value at the t address ( t is a pointer type)
It is composed of one method named MarshalJSON with the signature () ([]byte, error) .
• A custom marshaller will output a slice of byte, which is the JSON marshaled
• And an error.
b := make([]byte, 0, len(RFC3339Nano)+2)
b = append(b, '"')
b = t.AppendFormat(b, RFC3339Nano)
b = append(b, '"')
return b, nil
}
Here is the implementation of json.Marshaller interface for the type Time . The idea is to transform a value of type Time into its JSON
representation.
• First, the year is checked; it must be between 0 and 9999 (no more than four digits)
◦ The time will be outputed following the format described in the constant RFC3339Nano
◦ 2 is added to the format’s length for handling the two double quotes (2 bytes).
• Then the time formatted is appended to the slice with the method AppendFormat
15 Test yourself
15.1 Questions
1. Which method can you use to convert a variable into JSON?
2. Which method can you use to convert a JSON string into a variable of a certain type?
16 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
15.2 Answers
1. Which method can you use to convert a variable into JSON?
json.Marshal
2. Which method can you use to convert a JSON string into a variable of a certain type?
json.Unmarshal
2. Ex :
2. ex : `xml:"id"`
2. The term encoding is used when you have to transform a data stream into a coded form
3. Marshaling is used when you have to transform a variable into a coded form
The paper and the digital edition of this book are available here. ×
I also filmed a video course to build a real world project with Go.
16 Key takeaways
• JSON (JavaScript Object Notation) is a popular format used to represent structured data easily readable by a human or machine.
• Struct tags allow you to modify how a field will be outputed in JSON/XMLName
17 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
• To get the value of a struct tag, you will have to use the reflect standard package
p:= Product{ID:32}
t := reflect.TypeOf(p)
for i := 0; i < t.NumField(); i++ {
fmt.Printf("field Name : %s\n",t.Field(i).Name)
fmt.Printf("field Tag : %s\n",t.Field(i).Tag)
}
• You can combine struct tags for JSON with other struct tags
1. Panic is not recommended in a real-life program; you should handle the error and use a proper exit logic↩
Bibliography
Previous Next
Table of contents
Did you spot an error ? Want to give me feedback ? Here is the feedback page! ×
Newsletter:
Like what you read ? Subscribe to the newsletter.
Practical Go Lessons
By Maximilien Andile
Copyright (c) 2023
Follow me Contents
Posts
Book
Support the author Video Tutorial
About
The author
Legal Notice
Feedback
18 of 19 02/01/2023, 02:14
JSON and XML - Practical Go Lessons https://www.practical-go-lessons.com/chap-25-json-and-xml
19 of 19 02/01/2023, 02:14