Skip to main content

Echo XML Response

Introduction

XML (eXtensible Markup Language) is a versatile data format used for structuring, storing, and transporting data. While JSON has become more popular in recent years, XML remains important in many enterprise environments, SOAP APIs, and legacy systems. In this tutorial, we'll explore how to create and send XML responses using the Echo framework.

XML responses allow your Echo application to communicate with clients that expect data in XML format. Understanding how to properly format and send XML is an essential skill for building flexible web applications that can integrate with various systems.

Understanding XML Responses in Echo

Echo makes it straightforward to send XML responses through its context (c *echo.Context) interface. The framework provides a built-in XML() method that handles the conversion of Go structures to XML and sets appropriate response headers.

Basic XML Response Structure

XML documents have a hierarchical structure with elements that can contain attributes, text, and other elements. A typical XML response looks like:

xml
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<message>Data retrieved successfully</message>
<data>
<user id="1">
<name>John Doe</name>
<email>[email protected]</email>
</user>
</data>
</response>

Creating XML Responses in Echo

Step 1: Define Go Structs with XML Tags

First, we need to define Go structs that will be marshaled into XML. We use struct tags to control how fields are represented in XML:

go
type User struct {
ID int `xml:"id,attr"`
Name string `xml:"name"`
Email string `xml:"email"`
}

type Response struct {
XMLName xml.Name `xml:"response"`
Status string `xml:"status"`
Message string `xml:"message"`
Data struct {
Users []User `xml:"user"`
} `xml:"data"`
}

The struct tags determine:

  • xml:"tagname" - The XML element name
  • xml:"tagname,attr" - An XML attribute instead of an element
  • xml:",omitempty" - Omit the field if it's empty

Step 2: Implement a Handler Function

Next, create a handler function that prepares your data and returns it as XML:

go
func GetUsersXML(c echo.Context) error {
// Create our response structure
response := Response{
Status: "success",
Message: "Users retrieved successfully",
}

// Add some sample users
response.Data.Users = []User{
{ID: 1, Name: "John Doe", Email: "[email protected]"},
{ID: 2, Name: "Jane Smith", Email: "[email protected]"},
}

// Return the response as XML
return c.XML(http.StatusOK, response)
}

Step 3: Register Your Route

Register the handler with an appropriate route in your Echo application:

go
func main() {
e := echo.New()

// Register the XML response handler
e.GET("/users/xml", GetUsersXML)

e.Logger.Fatal(e.Start(":1323"))
}

Complete Example

Let's put everything together in a complete example:

go
package main

import (
"encoding/xml"
"net/http"

"github.com/labstack/echo/v4"
)

type User struct {
ID int `xml:"id,attr"`
Name string `xml:"name"`
Email string `xml:"email"`
}

type Response struct {
XMLName xml.Name `xml:"response"`
Status string `xml:"status"`
Message string `xml:"message"`
Data struct {
Users []User `xml:"user"`
} `xml:"data"`
}

func GetUsersXML(c echo.Context) error {
response := Response{
Status: "success",
Message: "Users retrieved successfully",
}

response.Data.Users = []User{
{ID: 1, Name: "John Doe", Email: "[email protected]"},
{ID: 2, Name: "Jane Smith", Email: "[email protected]"},
}

return c.XML(http.StatusOK, response)
}

func main() {
e := echo.New()

e.GET("/users/xml", GetUsersXML)

e.Logger.Fatal(e.Start(":1323"))
}

Output

When you make a GET request to /users/xml, you'll receive this XML response:

xml
<?xml version="1.0" encoding="UTF-8"?>
<response>
<status>success</status>
<message>Users retrieved successfully</message>
<data>
<user id="1">
<name>John Doe</name>
<email>[email protected]</email>
</user>
<user id="2">
<name>Jane Smith</name>
<email>[email protected]</email>
</user>
</data>
</response>

Working with XML in Real-World Applications

Integration with Legacy Systems

Many enterprise systems and older APIs require XML. Here's an example of integrating with a SOAP service:

go
func IntegrateWithLegacySystem(c echo.Context) error {
// Create SOAP envelope
soapReq := struct {
XMLName xml.Name `xml:"soap:Envelope"`
SoapNS string `xml:"xmlns:soap,attr"`
BodyNS string `xml:"xmlns:m,attr"`
SoapBody struct {
GetStockPrice struct {
Symbol string `xml:"m:symbol"`
} `xml:"m:GetStockPrice"`
} `xml:"soap:Body"`
}{
SoapNS: "http://www.w3.org/2003/05/soap-envelope",
BodyNS: "http://www.example.org/stock",
}

soapReq.SoapBody.GetStockPrice.Symbol = "GOOG"

// In a real app, you'd send this XML to your SOAP service
// and parse the response

// For demo purposes, create a mock response
response := Response{
Status: "success",
Message: "Stock price retrieved",
}

return c.XML(http.StatusOK, response)
}

XML Configuration Files

XML is often used for configuration files. Here's how you might load XML configuration:

go
type AppConfig struct {
XMLName xml.Name `xml:"config"`
ServerPort int `xml:"server>port"`
DatabaseURL string `xml:"database>url"`
LogLevel string `xml:"logging>level"`
}

func LoadConfig() (AppConfig, error) {
var config AppConfig
data, err := ioutil.ReadFile("config.xml")
if err != nil {
return config, err
}

err = xml.Unmarshal(data, &config)
return config, err
}

Advanced XML Features

Custom XML Marshaling

For more complex XML structures, you can implement the MarshalXML interface:

go
func (u User) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
start.Name = xml.Name{Local: "user"}
start.Attr = append(start.Attr, xml.Attr{
Name: xml.Name{Local: "id"},
Value: strconv.Itoa(u.ID),
})

err := e.EncodeToken(start)
if err != nil {
return err
}

// Encode name element
err = e.EncodeElement(u.Name, xml.StartElement{Name: xml.Name{Local: "name"}})
if err != nil {
return err
}

// Encode email element
err = e.EncodeElement(u.Email, xml.StartElement{Name: xml.Name{Local: "email"}})
if err != nil {
return err
}

return e.EncodeToken(xml.EndElement{Name: start.Name})
}

Setting XML Headers and Indentation

You can customize the XML output by creating a custom XML response function:

go
func CustomXML(c echo.Context, code int, i interface{}) error {
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationXML)
c.Response().WriteHeader(code)

output, err := xml.MarshalIndent(i, "", " ")
if err != nil {
return err
}

// Add XML header
output = append([]byte(xml.Header), output...)
_, err = c.Response().Write(output)
return err
}

Error Handling

Proper error handling is important when working with XML:

go
func GetUserXML(c echo.Context) error {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
// Return XML error response
errorResp := struct {
XMLName xml.Name `xml:"error"`
Code int `xml:"code"`
Message string `xml:"message"`
}{
Code: 400,
Message: "Invalid user ID",
}

return c.XML(http.StatusBadRequest, errorResp)
}

// Continue processing...
user, err := findUser(id)
if err != nil {
// Another error response
return c.XML(http.StatusNotFound, struct {
XMLName xml.Name `xml:"error"`
Code int `xml:"code"`
Message string `xml:"message"`
}{
Code: 404,
Message: "User not found",
})
}

return c.XML(http.StatusOK, user)
}

Summary

In this tutorial, we've explored how to create and send XML responses using the Echo framework. You've learned:

  1. How to define Go structures with XML tags for proper XML formatting
  2. How to use Echo's built-in XML() method to send XML responses
  3. Real-world applications of XML responses
  4. Advanced XML techniques including custom marshaling and indentation
  5. Proper error handling with XML responses

XML responses are valuable for integrating with legacy systems, enterprise applications, and clients that specifically require XML data. By understanding how to properly format and send XML using Echo, you've added an important skill to your web development toolkit.

Additional Resources

Exercises

  1. Create an Echo endpoint that returns a list of products as XML
  2. Extend the XML response to include pagination metadata (total items, page number, etc.)
  3. Implement a handler that can accept both XML and JSON requests and respond in the same format
  4. Build an XML-based configuration system for your Echo application
  5. Create a middleware that logs all outgoing XML responses for debugging


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)