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.
Encode / Decode
Marshal / Unmarshal
Data stream
JSON
XML
interface implementation
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])
\n\nMost 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 :
\ntype Cart struct {\n ID string\n Paid bool\n}\ncart := Cart{\n ID: "121514",\n Paid: false,\n}
\nWhat 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”.
\nIn this section, we will study two “coded forms” : JSON and XML.
\n\nIn the next section, you will see the term Marshal and Unmarshal appear. Marshaling and unmarshaling also denote a process.
\nMarshaling is the process of transforming an object into a storable representation using a specific format.
\nUnmarshaling 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.
\nYou 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.
\nConclusion : we encode a stream of data, we marshal a data structure.
\n\nJSON 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.
\nHere is an example :
\n[\n {\n "id": 1,\n "first_name": "Annalise",\n "last_name": "Marlin",\n "city": "Bang Bon"\n },\n {\n "id": 2,\n "first_name": "Dacy",\n "last_name": "Biffin",\n "city": "Cuilco"\n },\n {\n "id": 3,\n "first_name": "Wendye",\n "last_name": "Taillard",\n "city": "Preobrazhenka"\n }\n]
\nWe 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.
\n"id": 3
To decode a string containing JSON, you will need to create type structs. Let’s take an example.
\nImagine that you want to decode the following JSON string :
\n{\n "cat": {\n "name": "Joey",\n "age": 8\n }\n}
\nWe 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 :
\ntype MyJson struct {\n Cat\n}
\nThen we will define the Cat
struct :
type Cat struct {\n Name string\n Age uint\n}
\nWe have two fields, name and age, similar to the JSON structure that we want to decode. We can now launch the decoding function :
\n// json-xml/unmarshall-json/main.go \n\nfunc main() {\n myJson := []byte(`{"cat":{ "name":"Joey", "age":8}}`)\n c := MyJson{}\n err := json.Unmarshal(myJson, &c)\n if err != nil {\n panic(err)\n }\n fmt.Println(c.Cat.Name)\n fmt.Println(c.Cat.Age)\n}
\nWe 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?
\nfmt.Println(c.Cat.Name)\nfmt.Println(c.Cat.Age)
\nWe expect to see :
\nJoey\n8
\nBut if you run the previous code, you will see that nothing is output! Why?
\nBecause 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.
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!
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.
\n\nTags 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.
\n// json-xml/unmarshall-json-tags/main.go \n\ntype MyJson struct {\n Cat `json:"cat"`\n}\n\ntype Cat struct {\n Name string `json:"name"`\n Age uint `json:"age"`\n}
\nYou see that we added here three tags to the fields of the type structs. Tags always have the same construction :
\n`json:"nameOfTheFieldInJson"`
\nThe 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
)
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.
\nWe first define the Product
type struct and the Category
type struct (an article belong to a unique category) :
type Product struct {\n ID uint64\n Name string\n SKU string\n Cat Category\n}\ntype Category struct {\n ID uint64\n Name string\n}
\nThen we will create a product, more precisely, a Tea Pot.
\np := Product{ID: 42, Name: "Tea Pot", SKU: "TP12", Category: Category{ID: 2, Name: "Tea"}}
\nOnce we have our variable, we can launch the json.Marshal function :
\n// json-xml/marshall-json/main.go \n\nb, err := json.Marshal(p)\nif err != nil {\n panic(err)\n}\nfmt.Println(string(b))
\nThe 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"}}
\nThe formatting of the string is not optimal. We can use instead of another function to encode the JSON but also to indent it :
\n// json-xml/marshall-json/main.go \n\nbI, err := json.MarshalIndent(p,""," ")\nif err != nil {\n panic(err)\n}\nfmt.Println(string(bI))
\nThe 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 :
\n{\n "ID": 42,\n "Name": "Tea Pot",\n "SKU": "TP12",\n "Category": {\n "ID": 2,\n "Name": "Tea"\n }\n}
\nUsually, 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 \n\ntype Product struct {\n ID uint64 `json:"id"`\n Name string `json:"name"`\n SKU string `json:"sku"`\n Category Category `json:"category"`\n}\ntype Category struct {\n ID uint64 `json:"id"`\n Name string `json:"name"`\n}
\nThe outputed result is now :
\n{\n "id": 42,\n "name": "Tea Pot",\n "sku": "TP12",\n "category": {\n "id": 2,\n "name": "Tea"\n }\n}
\n\nIn 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.
\n\nIf 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
:
// json-xml/tags/main.go \n\ntype Product struct {\n ID uint64 `json:"id"`\n Name string `json:"name"`\n}\n\nfunc main() {\n p := Product{ID: 42}\n bI, err := json.MarshalIndent(p, "", " ")\n if err != nil {\n panic(err)\n }\n fmt.Println(string(bI))\n}
\nThe script will print :
\n{\n "id": 42,\n "name": ""\n}
\nHere 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 {\n ID uint64 `json:"id"`\n Name string `json:"name,omitempty"`\n}
\nThe result is now :
\n{\n "id": 42\n}
\nThe name property has been omitted because it was empty.
\n\nThe 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.
\nThere is a directive for that : -
// json-xml/tags/main.go \n\ntype Product struct {\n ID uint64 `json:"id"`\n Name string `json:"name,omitempty"`\n Description string `json:"-"`\n}
\nHere we are telling the json package not to output the field Description
in the encoded string.
You can obtain the value of tags in a type struct by using the package reflect :
\n// json-xml/access-tags/main.go \n\ntype Product struct {\n ID uint64 `json:"id"`\n Name string `json:"name,omitempty"`\n Description string `json:"-"`\n}\n\nfunc main() {\n p:= Product{ID:32}\n t := reflect.TypeOf(p)\n for i := 0; i < t.NumField(); i++ {\n fmt.Printf("field Name : %s\\n",t.Field(i).Name)\n fmt.Printf("field Tag : %s\\n",t.Field(i).Tag)\n }\n}
\nHere 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\n// file: type.go\ntype StructField struct {\n Name string\n PkgPath string\n Type Type\n Tag StructTag\n Offset uintptr\n Index []int\n Anonymous bool\n}
\nThe 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 :
//...\n// for loop\n fmt.Println(t.Field(i).Tag.Get("json"))\n//...
\nIn the context of the previous code snippet this will output :
\nid\nname,omitempty\n-
\nAnd 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 \n\nif tagValue, ok := t.Field(i).Tag.Lookup("test"); ok {\n fmt.Println(tagValue)\n} else {\n fmt.Println("no tag 'test'")\n}
\n\nJSON and XML tags are convenient; you might want to create your own struct tags for special use cases. The language expects a certain syntax:
\nA 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.
\nIf we follow this model, we can create the following tag with three keys.
\n`myKey1:"test,test2" myKey2:"yo" myKey3:"yoyooo"`
\n\nXML 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.
\nFor instance, this is the XML encoded product object :
\n<Product>\n <id>42</id>\n <name>Tea Pot</name>\n <sku>TP12</sku>\n <category>\n <id>2</id>\n <name>Tea</name>\n </category>\n</Product>
\nAnd here is its equivalent in JSON :
\n{\n "id": 42,\n "name": "Tea Pot",\n "sku": "TP12",\n "category": {\n "id": 2,\n "name": "Tea"\n }\n}
\nJSON needs 95 chars
XML needs 111 chars to encode the same object
Decode XML is as easy as decode JSON.
\nFirst, you define a struct that will hold your data :
\n// json-xml/xml/decode/main.go \n\ntype MyXML struct {\n Cat `xml:"cat"`\n}\n\ntype Cat struct {\n Name string `xml:"name"`\n Age uint `xml:"age"`\n}
\nWe added XML struct tags to the struct (il allow the decoder to know where to find the fields’ values).
\nThen you can call the xml.Unmarshal
function :
// json-xml/xml/decode/main.go \n\nfunc main() {\n myXML := []byte(`<cat>\n <name>Ti</name>\n <age>23</age>\n</cat>`)\n c := MyXML{}\n err := xml.Unmarshal(myXML, &c)\n if err != nil {\n panic(err)\n }\n fmt.Println(c.Cat.Name)\n fmt.Println(c.Cat.Age)\n}
\nEncoding 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 \n\nfunc main() {\n p := Product{ID: 42, Name: "Tea Pot", SKU: "TP12", Price: 30.5, Category: Category{ID: 2, Name: "Tea"}}\n bI, err := xml.MarshalIndent(p, "", " ")\n if err != nil {\n panic(err)\n }\n xmlWithHeader := xml.Header + string(bI)\n fmt.Println(xmlWithHeader)\n}
\nRemember that you have to add a header to your XML :
\n<?xml version="1.0" encoding="UTF-8"?>
\nThis 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.
\nThe xml package defines a constant that you can use directly :
\nxmlWithHeader := xml.Header + string(bI)
\nHere is the XML produced :
\n<?xml version="1.0" encoding="UTF-8"?>\n<Product>\n <id>42</id>\n <name>Tea Pot</name>\n <sku>TP12</sku>\n <price>30.5</price>\n <category>\n <id>2</id>\n <name>Tea</name>\n </category>\n</Product>
\n\nIf you create a variable of the following type
\ntype MyXmlElement struct {\n Name string `xml:"name"`\n}
\nand if you marshal it, the result will be :
\n<MyXmlElement>\n <name>Testing</name>\n</MyXmlElement>
\nWe 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 :
\ntype MyXmlElement struct {\n XMLName xml.Name `xml:"myOwnName"`\n Name string `xml:"name"`\n}
\nThe 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 :
\n// json-xml/xml/xmlNameType/main.go\npackage main\n\nimport (\n "encoding/xml"\n "fmt"\n)\n\ntype MyXmlElement struct {\n XMLName xml.Name `xml:"myOwnName"`\n Name string `xml:"name"`\n}\n\nfunc main() {\n elem := MyXmlElement{Name: "Testing"}\n m, err := xml.MarshalIndent(elem, "", " ")\n if err != nil {\n panic(err)\n }\n fmt.Println(string(m))\n\n}
\nThis program will output :
\n<myOwnName>\n <name>Testing</name>\n</myOwnName>
\n\nIn addition to the omit empty (omitempty
) and the ignore directive (“-
”) the standard library offers you a large palette of tag values :
You can add specific attributes to a field with XML. For instance :
\n<price currency="EUR">123</price>
\nHere 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 {\n Text string `xml:",chardata"`\n Currency string `xml:"currency,attr"`\n}\n\ntype Product struct {\n Price Price `xml:"price"`\n Name string `xml:"name"`\n}
\nWe 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"`
\nIt tells Go to get the data contained between the tags opening and the closingprice
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"`
\nYou always begin with the name of the attribute.
\nIf 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.
\nLet’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>!
<product>\n <price currency="EUR">123</price>\n <myXml><![CDATA[<yo>12</yo>]]></myXml>\n <name>Kit</name>\n</product>
\nHere is a valid type struct that will allow you to retrieve the value of myXml :
\n// json-xml/xml/CDATA/main.go \n\ntype Product struct {\n Price Price `xml:"price"`\n Name string `xml:"name"`\n MyXml MyXml `xml:"myXml"`\n}\n\ntype MyXml struct {\n Text string `xml:",cdata"`\n}
\nNote that we created the type struct MyXml
that has a single field Text
. This single field has the following tag :
`xml:",cdata"`
\n\nUnlike JSON, you can add comments in your XML :
\n// json-xml/xml/comments/main.go \n\ntype Price struct {\n Text string `xml:",chardata"`\n Currency string `xml:"currency,attr"`\n}\n\ntype Product struct {\n Comment string `xml:",comment"`\n Price Price `xml:"price"`\n Name string `xml:"name"`\n}
\nHere 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 \n\nc := Product{}\nc.Comment = "this is my comment"\nc.Price = Price{Text : "123",Currency:"EUR"}\nc.Name = "testing"\n\nb, err := xml.MarshalIndent(c,""," ")\nif err != nil {\n panic(err)\n}\nfmt.Println(string(b))
\nThis script will output :
\n<Product>\n <!--this is my comment-->\n <price currency="EUR">123</price>\n <name>testing</name>\n</Product>
\n\nA simple field can be nested into several XML elements :
\n// json-xml/xml/nesting/main.go\npackage main\n\nimport (\n "encoding/xml"\n "fmt"\n)\n\ntype Product struct {\n Name string `xml:"first>second>third"`\n}\n\nfunc main() {\n c := Product{}\n c.Name = "testing"\n b, err := xml.MarshalIndent(c, "", " ")\n if err != nil {\n panic(err)\n }\n fmt.Println(string(b))\n}
\nThe previous script will output :
\n<Product>\n <first>\n <second>\n <third>testing</third>\n </second>\n </first>\n</Product>
\nYou 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 :
\n`xml:"first>second>third"`
\n\nBuilding 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 :
\nFor JSON :
\nhttps://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 :
\nhttps://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
In case you to use a specific logic for marshaling a type to JSON, you can create your custom Marshaler and Unmarshaler.
\n\nTo create a custom JSON unmarshaler, you will have to implement the json.Unmarshaler
interface.
Here is the definition of the interface :
\ntype Unmarshaler interface {\n UnmarshalJSON([]byte) error\n}
\nIt has a unique method specified : UnmarshalJSON([]byte) error
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 :
[\n {\n "id": 1,\n "first_name": "Hedvig",\n "last_name": "Largen",\n "created_at": "2020-07-02T08:41:35Z"\n },\n {\n "id": 2,\n "first_name": "Win",\n "last_name": "Alloisi",\n "created_at": "2020-02-21T12:36:41Z"\n }\n]
\nusers
(a slice of User
):type User struct {\n ID int `json:"id"`\n Firstname string `json:"first_name"`\n Lastname string `json:"last_name"`\n CreatedDate time.Time `json:"created_at"`\n}\nusers := make([]User,2)
\nFor each type, Go will check if it has a custom unmarshaler.
\nThe 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.\n// The time is expected to be a quoted string in RFC 3339 format.\nfunc (t *Time) UnmarshalJSON(data []byte) error {\n // Ignore null, like in the main JSON package.\n if string(data) == "null" {\n return nil\n }\n // Fractional seconds are handled implicitly by Parse.\n var err error\n *t, err = Parse(`"`+RFC3339+`"`, string(data))\n return err\n}
\nIn this method, the value null is ignored.
\n\"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 :
\n*t, err = Parse(`"`+RFC3339+`"`, string(data))
\nThis 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.
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
*
to set the value at the t
address (t
is a pointer type)Here is the interface that you will need to implement on your type :
\ntype Marshaler interface {\n MarshalJSON() ([]byte, error)\n}
\nIt 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.
// MarshalJSON implements the json.Marshaler interface.\n// The time is a quoted string in RFC 3339 format, with sub-second precision added if present.\nfunc (t Time) MarshalJSON() ([]byte, error) {\n if y := t.Year(); y < 0 || y >= 10000 {\n // RFC 3339 is clear that years are four digits exactly.\n // See golang.org/issue/4556#c15 for more discussion.\n return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")\n }\n\n b := make([]byte, 0, len(RFC3339Nano)+2)\n b = append(b, '"')\n b = t.AppendFormat(b, RFC3339Nano)\n b = append(b, '"')\n return b, nil\n}
\nHere 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 constantRFC3339Nano
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)
Which method can you use to convert a variable into JSON?
Which method can you use to convert a JSON string into a variable of a certain type?
How to change how a field name on a struct is displayed into JSON?
How to change how a field name on a struct is displayed into XML?
What is the difference between encoding and marshaling?
How to customize the JSON output of a type?
How to customize how Go will parse an element of a given type in JSON?
Which method can you use to convert a variable into JSON?
\njson.Marshal
Which method can you use to convert a JSON string into a variable of a certain type?
\njson.Unmarshal
How to change how a field name on a struct is displayed into JSON?
\nWith a struct tag
Ex :
type User struct {\n ID int `json:"id"`\n}
\n`json:\"id\"`
How to change how a field name on a struct is displayed into XML?
\nWith a struct tag
ex : `xml:\"id\"`
What is the difference between encoding and marshaling?
\nEncoding and marshaling refer to the same process
The term encoding is used when you have to transform a data stream into a coded form
Marshaling is used when you have to transform a variable into a coded form
How to customize the JSON representation of a type?
\njson.Marshaler
interface on your typeHow to customize how Go will parse an element of a given type in JSON?
\njson.Unmarshaler
interface on your typeJSON (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
\ntype Product struct {\n Price Price `xml:"price"`\n Name string `xml:"name"`\n MyXml MyXml `xml:"myXml"`\n}
To get the value of a struct tag, you will have to use the reflect
standard package
p:= Product{ID:32}\nt := reflect.TypeOf(p)\nfor i := 0; i < t.NumField(); i++ {\n fmt.Printf("field Name : %s\\n",t.Field(i).Name)\n fmt.Printf("field Tag : %s\\n",t.Field(i).Tag)\n}
Some specific struct tags values
\nSkip 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
\ntype User struct {\n ID int `json:"id" xml:"id"`\n}
Panic is not recommended in a real-life program; you should handle the error and use a proper exit logic↩︎
Previous
\n\t\t\t\t\t\t\t\t\tAnonymous functions & closures
\n\t\t\t\t\t\t\t\tNext
\n\t\t\t\t\t\t\t\t\tBasic HTTP Server
\n\t\t\t\t\t\t\t\t