Lahiru Udayanga
Lahiru Udayanga

Reputation: 369

Unmarshaling nested xml golang

I want to unmarshall a nested xml to a golang struct. The xml that I want to unmarshall is shown below.

<?xml version="1.0" encoding="UTF-8"?>
<status>
    <authorized>true</authorized>
    <plan>Basic</plan>
    <usage_reports>
        <usage_report metric="hits" period="day">
            <period_start>2021-07-20 00:00:00 +0000</period_start>
            <period_end>2021-07-21 00:00:00 +0000</period_end>
            <max_value>1000000</max_value>
            <current_value>0</current_value>
        </usage_report>
        <usage_report metric="hits.82343" period="day">
            <period_start>2021-07-20 00:00:00 +0000</period_start>
            <period_end>2021-07-21 00:00:00 +0000</period_end>
            <max_value>1000000</max_value>
            <current_value>0</current_value>
        </usage_report>
    </usage_reports>
</status>

To unmarshall I have defined two go structs as follows.

// UsageReport represents the usage report of a particular metric.
type UsageReport struct {
    Usage   string `xml:"usage_report,chardata"`
    Metric  string `xml:"metric,attr"`
    Period  string `xml:"period,attr"`
    Start   string `xml:"period_start"`
    End     string `xml:"period_end"`
    Max     int64  `xml:"max_value"`
    Current int64  `xml:"current_value"`
}

// AuthResponse represents the structure of the response from authorize calls.
type AuthResponse struct {
    Status     xml.Name      `xml:"status"`
    Authorized bool          `xml:"authorized"`
    Plan       string        `xml:"plan"`
    Usages     []UsageReport `xml:"usage_reports"`
}

But when I try to unmarshall using xml.Unmarshal, only Plan and Authorized values gets unmarshalled. The result of unmarshalling is shown below.

XML: {{ } true Basic []}

Upvotes: 1

Views: 862

Answers (2)

Gopher
Gopher

Reputation: 751

In order to get the value of Usages field modify xml:"usage_reports" to xml:"usage_reports>usage_report" as per the suggestions of mkopriva.

package main

import (
    "encoding/xml"
    "fmt"
)

type UsageReport struct {
    Metric  string `xml:"metric,attr"`
    Period  string `xml:"period,attr"`
    Start   string `xml:"period_start"`
    End     string `xml:"period_end"`
    Max     int64  `xml:"max_value"`
    Current int64  `xml:"current_value"`
}

type AuthResponse struct {
    Status     xml.Name      `xml:"status"`
    Authorized bool          `xml:"authorized"`
    Plan       string        `xml:"plan"`
    Usages     []UsageReport `xml:"usage_reports>usage_report"`
}

var data = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<status>
    <authorized>true</authorized>
    <plan>Basic</plan>
    <usage_reports>
        <usage_report metric="hits" period="day">
            <period_start>2021-07-20 00:00:00 +0000</period_start>
            <period_end>2021-07-21 00:00:00 +0000</period_end>
            <max_value>1000000</max_value>
            <current_value>0</current_value>
        </usage_report>
        <usage_report metric="hits.82343" period="day">
            <period_start>2021-07-20 00:00:00 +0000</period_start>
            <period_end>2021-07-21 00:00:00 +0000</period_end>
            <max_value>1000000</max_value>
            <current_value>0</current_value>
        </usage_report>
    </usage_reports>
</status>
`)

func main() {
    r := AuthResponse{}
    if err := xml.Unmarshal(data, &r); err != nil {
        panic(err)
    }
    fmt.Println(r)
}

Output:

{{ } true Basic [{hits day 2021-07-20 00:00:00 +0000 2021-07-21 00:00:00 +0000 1000000 0} {hits.82343 day 2021-07-20 00:00:00 +0000 2021-07-21 00:00:00 +0000 1000000 0}]}

Upvotes: 1

Clark McCauley
Clark McCauley

Reputation: 1457

Try this go playground. I use this tool to cheat and help me generate Go types from XML data.

package main

import (
    "encoding/xml"
    "fmt"
)

type Status struct {
    XMLName      xml.Name `xml:"status"`
    Text         string   `xml:",chardata"`
    Authorized   string   `xml:"authorized"`
    Plan         string   `xml:"plan"`
    UsageReports struct {
        Text        string `xml:",chardata"`
        UsageReport []struct {
            Text         string `xml:",chardata"`
            Metric       string `xml:"metric,attr"`
            Period       string `xml:"period,attr"`
            PeriodStart  string `xml:"period_start"`
            PeriodEnd    string `xml:"period_end"`
            MaxValue     string `xml:"max_value"`
            CurrentValue string `xml:"current_value"`
        } `xml:"usage_report"`
    } `xml:"usage_reports"`
}

func main() {
    status := Status{}
    err := xml.Unmarshal([]byte(xmlStr), &status)
    if err != nil {
        panic(err)
    }
    fmt.Println(status.UsageReports.UsageReport[0].MaxValue)
}

const xmlStr = `<?xml version="1.0" encoding="UTF-8"?><status><authorized>true</authorized><plan>Basic</plan><usage_reports><usage_report metric="hits" period="day"><period_start>2021-07-20 00:00:00 +0000</period_start><period_end>2021-07-21 00:00:00 +0000</period_end><max_value>1000000</max_value><current_value>0</current_value></usage_report><usage_report metric="hits.82343" period="day"><period_start>2021-07-20 00:00:00 +0000</period_start><period_end>2021-07-21 00:00:00 +0000</period_end><max_value>1000000</max_value><current_value>0</current_value></usage_report></usage_reports></status>`

Upvotes: 2

Related Questions