0% found this document useful (0 votes)
5 views

Go Json-Xml

This document discusses JSON and XML formats for encoding and decoding data. It covers concepts like encoding, decoding, marshaling, unmarshaling and provides examples of converting Go variables to JSON and XML as well as converting JSON and XML strings back to Go variables. The document also provides details on JSON and XML syntax and structure.

Uploaded by

prsnortin
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

Go Json-Xml

This document discusses JSON and XML formats for encoding and decoding data. It covers concepts like encoding, decoding, marshaling, unmarshaling and provides examples of converting Go variables to JSON and XML as well as converting JSON and XML strings back to Go variables. The document also provides details on JSON and XML syntax and structure.

Uploaded by

prsnortin
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 19

JSON and XML - Practical Go Lessons https://www.practical-go-lessons.

com/chap-25-json-and-xml

Chapter 25: JSON and XML

1 What will you learn in this chapter?


• What is encoding/decoding?

• What are JSON and XML?

• What are marshaling and unmarshaling?

• How to convert a variable into JSON and XML.

• How to convert a JSON or XML string (or file) into a variable.

2 Technical concepts covered


• Encode / Decode

• 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

type Cart struct {


ID string
Paid bool
}
cart := Cart{
ID: "121514",
Paid: false,
}

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.

• Encoding is the process of transforming a piece of data into a “coded form”.

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.

◦ We can marshal a variable of type struct in JSON, for example


• Unmarshaling is the reverse process.

“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.

Conclusion : we encode a stream of data, we marshal a data structure.

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"
}
]

• We have an array of 3 user objects.

• The array starts with [ and ends with ]

• Each object starts with a curly brace and ends with a curly brace. ( {} )

• Each user property is written with a key-value pair format.

"id": 3

7 Unmarshal JSON
To decode a string containing JSON, you will need to create type structs. Let’s take an example.

Imagine that you want to decode the following JSON string :

{
"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 will first create a struct that holds the cat struct :

type MyJson struct {


Cat
}

Then we will define the Cat struct :

type Cat struct {


Name string
Age uint
}

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 :

• A slice of bytes that represent the JSON to decode ( myJson )

• A pointer to the variable c ( &c )

Why do we need to pass a pointer? Because the Unmarshal function will modify the value c .

What is the expected output of the following code?

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.

Solution 2 seems difficult. In reality, it’s not. We will use tags.

7.0.0.2 Struct Tags


Tags are small string literals that can follow a field declaration. Tags are accessible to the decoder. It will use them to decode the JSON string
correctly.

// json-xml/unmarshall-json-tags/main.go

type MyJson struct {


Cat `json:"cat"`
}

type Cat struct {


Name string `json:"name"`
Age uint `json:"age"`
}

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

Type Struct to JSON with struct tags

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) :

type Product struct {


ID uint64
Name string
SKU string
Cat Category
}
type Category struct {
ID uint64
Name string
}

Then we will create a product, more precisely, a Tea Pot.

p := Product{ID: 42, Name: "Tea Pot", SKU: "TP12", Category: Category{ID: 2, Name: "Tea"}}

Once we have our variable, we can launch the json.Marshal function :

// 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

bI, err := json.MarshalIndent(p,""," ")


if err != nil {
panic(err)
}
fmt.Println(string(bI))

The arguments of json.MarshalIndent are :

• the data to marshal

• 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.

• the indent string. In the example, we put a tab.

The outputed result is :

{
"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

type Product struct {


ID uint64 `json:"id"`
Name string `json:"name"`
SKU string `json:"sku"`
Category Category `json:"category"`
}
type Category struct {
ID uint64 `json:"id"`
Name string `json:"name"`
}

The outputed result is now :

{
"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.

9.1 How to ignore empty fields?


If one of the fields of your variable is not set, it will have the default zero value. By default, it will be present in the JSON string generated by
json.Marshal or json.MarshalIndent :

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

type Product struct {


ID uint64 `json:"id"`
Name string `json:"name"`
}

func main() {
p := Product{ID: 42}
bI, err := json.MarshalIndent(p, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(bI))
}

The script will print :

{
"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
:

type Product struct {


ID uint64 `json:"id"`
Name string `json:"name,omitempty"`
}

The result is now :

{
"id": 42
}

The name property has been omitted because it was empty.

9.2 Skip a field


The use case is simple, you have a struct with ten fields, and you want to hide it in the encoded version of your JSON string (for instance, the
field is no longer used by your clients). What you could do is to remove it from your struct. That solution is not the good one if you still use
this field in the rest of your code.

There is a directive for that : -

// json-xml/tags/main.go

type Product struct {


ID uint64 `json:"id"`
Name string `json:"name,omitempty"`
Description string `json:"-"`
}

Here we are telling the json package not to output the field Description in the encoded string.

9.3 How to parse the tags of your struct


You can obtain the value of tags in a type struct by using the package reflect :

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

type Product struct {


ID uint64 `json:"id"`
Name string `json:"name,omitempty"`
Description string `json:"-"`
}

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()

Lookup() Let’s take an example for the Get method :

//...
// for loop
fmt.Println(t.Field(i).Tag.Get("json"))
//...

In the context of the previous code snippet this will output :

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'")
}

9.4 How to create your tags


JSON and XML tags are convenient; you might want to create your own struct tags for special use cases. The language expects a certain
syntax:

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

Struct Tag format[fig:Struct-Tag-format]

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.

`myKey1:"test,test2" myKey2:"yo" myKey3:"yoyooo"`

10 Marshal / Unmarshal XML


XML means e Xtensible Markup Language. It’s a file format that is designed to store information in a structured way. It’s still used today in
certain domain, but it tends to decline in usage; mainly because it’s verbose (the messages encoded in XML need more space to be stored
than JSON, for instance.

For instance, this is the XML encoded product object :

<Product>
<id>42</id>
<name>Tea Pot</name>
<sku>TP12</sku>
<category>
<id>2</id>
<name>Tea</name>
</category>
</Product>

And here is its equivalent in JSON :

{
"id": 42,
"name": "Tea Pot",
"sku": "TP12",
"category": {
"id": 2,
"name": "Tea"
}
}

• JSON needs 95 chars

• XML needs 111 chars to encode the same object

Decode XML is as easy as decode JSON.

First, you define a struct that will hold your data :

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

type MyXML struct {


Cat `xml:"cat"`
}

type Cat struct {


Name string `xml:"name"`
Age uint `xml:"age"`
}

We added XML struct tags to the struct (il allow the decoder to know where to find the fields’ values).

Then you can call the xml.Unmarshal function :

// 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)
}

Remember that you have to add a header to your XML :

<?xml version="1.0" encoding="UTF-8"?>

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 :

xmlWithHeader := xml.Header + string(bI)

Here is the XML produced :

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

<?xml version="1.0" encoding="UTF-8"?>


<Product>
<id>42</id>
<name>Tea Pot</name>
<sku>TP12</sku>
<price>30.5</price>
<category>
<id>2</id>
<name>Tea</name>
</category>
</Product>

11 xml.Name type
If you create a variable of the following type

type MyXmlElement struct {


Name string `xml:"name"`
}

and if you marshal it, the result will be :

<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 :

type MyXmlElement struct {


XMLName xml.Name `xml:"myOwnName"`
Name string `xml:"name"`
}

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).

Let’s take an example :

// json-xml/xml/xmlNameType/main.go
package main

import (
"encoding/xml"
"fmt"
)

type MyXmlElement struct {


XMLName xml.Name `xml:"myOwnName"`
Name string `xml:"name"`
}

func main() {
elem := MyXmlElement{Name: "Testing"}
m, err := xml.MarshalIndent(elem, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(m))

This program will output :

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 Specificity of XML tags


In addition to the omit empty ( omitempty ) and the ignore directive (“ - ”) the standard library offers you a large palette of tag values :

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 :

type Price struct {


Text string `xml:",chardata"`
Currency string `xml:"currency,attr"`
}

type Product struct {


Price Price `xml:"price"`
Name string `xml:"name"`
}

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"`

You always begin with the name of the attribute.

XML attribute and struct tags

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

type Product struct {


Price Price `xml:"price"`
Name string `xml:"name"`
MyXml MyXml `xml:"myXml"`
}

type MyXml struct {


Text string `xml:",cdata"`
}

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

type Price struct {


Text string `xml:",chardata"`
Currency string `xml:"currency,attr"`
}

type Product struct {


Comment string `xml:",comment"`
Price Price `xml:"price"`
Name string `xml:"name"`
}

Here we added a field named Comment to our type struct Product . The special comment tag is added to the field.

Let’s take create a Product and marshal it in XML :

// json-xml/xml/comments/main.go

c := Product{}
c.Comment = "this is my comment"
c.Price = Price{Text : "123",Currency:"EUR"}
c.Name = "testing"

b, err := xml.MarshalIndent(c,""," ")


if err != nil {
panic(err)
}
fmt.Println(string(b))

This script will output :

<Product>
<!--this is my comment-->
<price currency="EUR">123</price>
<name>testing</name>
</Product>

12.4 Nested fields


A simple field can be nested into several XML elements :

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"
)

type Product struct {


Name string `xml:"first>second>third"`
}

func main() {
c := Product{}
c.Name = "testing"
b, err := xml.MarshalIndent(c, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(b))
}

The previous script will output :

<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"

That’s because we specified it in our struct tag :

`xml:"first>second>third"`

13 Struct type generators


Building a struct can be time consuming. Especially if you have to deal with complex JSON/XML structures. I can advise you to use those two
tools :

• For JSON :

◦ https://github.com/mholt/json-to-go has been built by Matt Holt.

◦ This is an online generator.

◦ Simply copy your JSON, paste it and let the tool generate the corresponding struct(s).

• For XML :

◦ https://github.com/miku/zek

◦ It has been made available by Martin Czygan.

◦ This is a CLI tool that allows you to generate structs based on sample XML

14 Custom JSON Marshaler / Unmarshaler (advanced)


In case you to use a specific logic for marshaling a type to JSON, you can create your custom Marshaler and Unmarshaler.

14.1 Custom Unmarshaler


To create a custom JSON unmarshaler, you will have to implement the json.Unmarshaler interface.

Here is the definition of the interface :

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

type Unmarshaler interface {


UnmarshalJSON([]byte) error
}

It has a unique method specified : UnmarshalJSON([]byte) error

14.1.1 Example: time.Time


Let’s take an example implementation for the time.Time type. JSON strings may contain time strings. Those time strings can be converted
to time.Time values :

[
{
"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 ):

type User struct {


ID int `json:"id"`
Firstname string `json:"first_name"`
Lastname string `json:"last_name"`
CreatedDate time.Time `json:"created_at"`
}
users := make([]User,2)

For each type, Go will check if it has a custom unmarshaler.

• The type time.Time defines a custom JSON unmarshaller.

• In other words, the type has a method named UnmarshalJSON with the signature ([]byte) error

• In other words time.Time implements the json.Unmarshaler interface

// UnmarshalJSON implements the json.Unmarshaler interface.


// The time is expected to be a quoted string in RFC 3339 format.
func (t *Time) UnmarshalJSON(data []byte) error {
// Ignore null, like in the main JSON package.
if string(data) == "null" {
return nil
}
// Fractional seconds are handled implicitly by Parse.
var err error
*t, err = Parse(`"`+RFC3339+`"`, string(data))
return err
}

• In this method, the value null is ignored.

◦ 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 :

*t, err = Parse(`"`+RFC3339+`"`, string(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 !

time.Parse returns the value parsed or an error

• The value parsed is assigned to *t

◦ We use the dereference operator * to set the value at the t address ( t is a pointer type)

14.2 Custom Marshaller


Here is the interface that you will need to implement on your type :

type Marshaler interface {


MarshalJSON() ([]byte, error)
}

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.

14.2.1 Example: time.Time


// MarshalJSON implements the json.Marshaler interface.
// The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
func (t Time) MarshalJSON() ([]byte, error) {
if y := t.Year(); y < 0 || y >= 10000 {
// RFC 3339 is clear that years are four digits exactly.
// See golang.org/issue/4556#c15 for more discussion.
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}

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)

• Then a slice of byte b is created.

◦ The slice has a length equal to 0, and a capacity of len(RFC3339Nano)+2 .

◦ 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).

• A single, double quote is then added to the slice

• Then the time formatted is appended to the slice with the method AppendFormat

• Another double quote is added to b (the closing one)

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?

3. How to change how a field name on a struct is displayed into JSON?

4. How to change how a field name on a struct is displayed into XML?

5. What is the difference between encoding and marshaling?

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

6. How to customize the JSON output of a type?

7. How to customize how Go will parse an element of a given type in JSON?

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

3. How to change how a field name on a struct is displayed into JSON?

1. With a struct tag

2. Ex :

type User struct {


ID int `json:"id"`
}

1. The struct tag is `json:"id"`


4. How to change how a field name on a struct is displayed into XML?

1. With a struct tag

2. ex : `xml:"id"`

5. What is the difference between encoding and marshaling?

1. Encoding and marshaling refer to the same process

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

6. How to customize the JSON representation of a type?

1. Implement the json.Marshaler interface on your type


7. How to customize how Go will parse an element of a given type in JSON?

1. Implement the json.Unmarshaler interface on your type

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.

• XML (e Xtensible Markup Language) is also used to represent structured data.

• “convert a variable into a coded form” = “marshaling”

• “convert a coded form into a variable” = “unmarshaling”

• “convert a data stream into a coded form” = “encoding”

• “convert a coded form into a data stream” = “decoding”

• To convert a variable into JSON/XML, we can use json.Marshal / xml.Marshal

• To convert JSON/XML into a variable, we can use json.Unmarshal / xml.Unmarshal

• 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

type Product struct {


Price Price `xml:"price"`
Name string `xml:"name"`
MyXml MyXml `xml:"myXml"`
}

• 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)
}

• Some specific struct tags values

◦ Skip a field : `json:"-"` (will be hidden in JSON, XML)

◦ Do not output empty values : `json:"first_name,omitempty"`

• You can combine struct tags for JSON with other struct tags

◦ For instance xml struct tags


type User struct {
ID int `json:"id" xml:"id"`
}

1. Panic is not recommended in a real-life program; you should handle the error and use a proper exit logic↩

Bibliography
Previous Next

Anonymous functions & closures Basic HTTP Server

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.

I will keep you informed about the book updates.

@ [email protected]

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

Buy paper or digital copy


Terms and Conditions

19 of 19 02/01/2023, 02:14

You might also like