Parsing XML and Converting to JSON using Golang

Introduction:

Recently I started to learn Golang for solving an interesting engineering problem and finally solved it successfully. Hence thought I can share the problem and solution with others who may also be interested to know. If this help’s someone that would be great.

Problem

Here is the sample XML which we are interested to decode using Golang,

<?xml version="1.0" encoding="utf-8"?>
<testreport id="Product Monitoring" name="Product Monitoring">
    <testclass id="Product Installation State" name="PRODUCT_INST_STATE">
        <testcase id="Product Installation State" name="PRODUCT_INST_STATE" tracker="INT">
            <result>Pass</result>
            <message>Script completed with no issues</message>
            <durationInSecs>32</durationInSecs>
        </testcase>
    </testclass>
    <testclass id="Product Windows Service State" name="PRODUCT_WIN_STATE">
        <testcase id="Product Windows Service State" name="PRODUCT_WIN_STATE" tracker="INT">
            <result>Pass</result>
            <message>Service running with no issues</message>
            <durationInSecs>80</durationInSecs>
        </testcase>
    </testclass>
    <testclass id="Product UI State" name="PRODUCT_UI_STATE">
        <testcase id="Product UI State" name="PRODUCT_UI_STATE" tracker="INT">
            <result>Pass</result>
            <message>Product UI state ON with no issues</message>
            <durationInSecs>7</durationInSecs>
        </testcase>
    </testclass>
</testreport>

How to solve it

The first and foremost we had to create struct’s to Unmarshal the xml to object. Here we have testreport as a root xml node along with testclass and testcase as sub node’s inside it. Here is how we can define structs for each node in child-to-parent hierarchy.

type testcase struct {
		Id             string `xml:"id,attr"`
		Name           string `xml:"name,attr"`
		Tracker        string `xml:"tracker,attr"`
		Result         string `xml:"result"`
		Message        string `xml:"message"`
		DurationInSecs string `xml:"durationInSecs"`
	}
	type testclass struct {
		Id       string   `xml:"id,attr"`
		Name     string   `xml:"name,attr"`
		TestCase testcase `xml:"testcase"`
	}
type testreport struct {
		Id        string      `xml:"id,attr"`
		Name      string      `xml:"name,attr"`
		TestClass []testclass `xml:"testclass"`
	}

Opening the xml in golang, xml file or any file for that matter can be opened using os.Open API available in “OS” package. Quick example on opening the xml file.

package main
import (
	"fmt"
	"io/ioutil"
	"os"
)
func main() {
	xmlFileName := "session.xml"
	xmlFile, err := os.Open(xmlFileName)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Successfully Opened file : ", xmlFileName)
	byteData, err := ioutil.ReadAll(xmlFile)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Printf("Overall file content :\n %s \n", byteData)
}

And the final part is Unmarshal the xml using the golang xml library and converting it to JSON. Golang provides xml support via package “encoding/xml” and JSON support via “encoding/json”.

Here is the final code putting together.

package main

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
)

func main() {
	type testcase struct {
		Id             string `xml:"id,attr"`
		Name           string `xml:"name,attr"`
		Tracker        string `xml:"tracker,attr"`
		Result         string `xml:"result"`
		Message        string `xml:"message"`
		DurationInSecs string `xml:"durationInSecs"`
	}
	type testclass struct {
		Id       string   `xml:"id,attr"`
		Name     string   `xml:"name,attr"`
		TestCase testcase `xml:"testcase"`
	}
	type testreport struct {
		Id        string      `xml:"id,attr"`
		Name      string      `xml:"name,attr"`
		TestClass []testclass `xml:"testclass"`
	}

	xmlFileName := "autosession.xml"
	xmlFile, err := os.Open(xmlFileName)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("Successfully Opened file", xmlFileName)
	byteData, err := ioutil.ReadAll(xmlFile)
	if err != nil {
		fmt.Println(err)
	}
	v := testreport{}
	err = xml.Unmarshal(byteData, &v)
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}
	result, err := json.Marshal(v)
	if nil != err {
		fmt.Println("Error marshalling to JSON", err)
		return
	}
	fmt.Printf("%s\n", result)
}

Here is the final input and output generated using code,

{
    "Id": "Product Monitoring",
    "Name": "Product Monitoring",
    "TestClass": [
        {
            "Id": "Product Installation State",
            "Name": "PRODUCT_INST_STATE",
            "TestCase": {
                "Id": "Product Installation State",
                "Name": "PRODUCT_INST_STATE",
                "Tracker": "INT",
                "Result": "Pass",
                "Message": "Script completed with no issues",
                "DurationInSecs": "32"
            }
        },
        {
            "Id": "Product Windows Service State",
            "Name": "PRODUCT_WIN_STATE",
            "TestCase": {
                "Id": "Product Windows Service State",
                "Name": "PRODUCT_WIN_STATE",
                "Tracker": "INT",
                "Result": "Pass",
                "Message": "Service running with no issues",
                "DurationInSecs": "80"
            }
        },
        {
            "Id": "Product UI State",
            "Name": "PRODUCT_UI_STATE",
            "TestCase": {
                "Id": "Product UI State",
                "Name": "PRODUCT_UI_STATE",
                "Tracker": "INT",
                "Result": "Pass",
                "Message": "Product UI state ON with no issues",
                "DurationInSecs": "7"
            }
        }
    ]
}

Conclusion

As newbie to Golang am really surprised how fast we can prototype something using Golang in short span of time. The tools required are already available and can make our life so much easy for modern programming challenges.

Will see you again, thanks