1

I am trying to write an XML parser which will load a data from XML file and convert it to golang structs, but apparently it is not working and I have no idea why. I wrote a test to troubleshoot it, but it didn't have me much clue. What I know is that XML is parsed, but the output Quote structs are empty.

I read in different topic that "The problem is that only exported fields can be marshaled/unmarshaled." but I have this covered - all fields starts with capital letter.

I run of ideas what I am doing wrong here. Any ideas?

Function code

package main

import (
    "encoding/xml"
    "fmt"
    "io"
    "log"
    "os"
)

type Quotes struct {
    Quote []Quote `xml:"quote"`
}

type Quote struct {
    Ver     string `xml:"ver"`
    Content string `xml:"content"`
    Author  string `xml:"author"`
}

func (sq *Quote) String() string {
    return fmt.Sprintf("%s - %s", sq.Author, sq.Content)
}

func LoadQuotes(pathToXmlFile string) ([]Quote, error) {

    xmlFile, err := os.Open(pathToXmlFile)
    if err != nil {
        log.Fatalf("%v", err)
    }
    defer xmlFile.Close()

    byteValue, _ := io.ReadAll(xmlFile)

        // Prints to help troubleshoot the issue
    fmt.Println("Read data:")
    fmt.Printf("%s\n", string(byteValue))
    fmt.Println("End of read data ---")

    var quotes Quotes
    if err := xml.Unmarshal(byteValue, &quotes); err != nil {
        log.Fatal(err)
        return nil, err
    }

    return quotes.Quote, nil
}

Test code

package main

import (
    "os"
    "testing"

    "github.com/stretchr/testify/require"
)

func TestLoadQuotes(t *testing.T) {
    // Create a temporary test file with some sample quotes
    testData := `<?xml version='1.0' encoding='utf-8'?>
    <quotes>
        <quote ver="1.0" content="Quote 1" author="Author 1"/>
        <quote ver="1.0" content="Quote 2" author="Author 2"/>
    </quotes>`

    tmpFile, err := os.CreateTemp("", "test.xml")
    if err != nil {
        t.Fatalf("Error creating temporary file: %v", err)
    }

    defer os.Remove(tmpFile.Name())

    _, err = tmpFile.Write([]byte(testData))
    if err != nil {
        t.Fatalf("Error writing to temporary file: %v", err)
    }
    tmpFile.Close()

    // Call the function with the temporary file
    quotes, err := LoadQuotes(tmpFile.Name())

    require.NoError(t, err)

    expected := []Quote{
        {Ver: "1.0", Content: "Quote 1", Author: "Author 1"},
        {Ver: "1.0", Content: "Quote 2", Author: "Author 2"},
    }

    require.Len(t, quotes, len(expected))

    for i, quote := range quotes {
                // HERE THE TEST FAILS, AUTHOR AND CONTENT IS EMPTY
        require.Equal(t, expected[i].String(), quote.String())
    }
}

Test result

Read data:
<?xml version='1.0' encoding='utf-8'?>
        <quotes>
                <quote ver="1.0" content="Quote 1" author="Author 1"/>
                <quote ver="1.0" content="Quote 2" author="Author 2"/>
        </quotes>
End of read data ---
--- FAIL: TestLoadQuotes (0.00s)
    xml_test.go:43: 
                Error Trace:    /home/sebastian/repo/blog/xml_test.go:43
                Error:          Not equal: 
                                expected: "Author 1 - Quote 1"
                                actual  : " - "
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -Author 1 - Quote 1
                                + - 
                Test:           TestLoadQuotes
FAIL
exit status 1
New contributor
oscarsierraproject is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

1 Answer 1

2

You need to declare the fields of your struct as XML attributes by adding attr. As is, they are interpreted as XML elements.

type Quote struct {
    Ver     string `xml:"ver,attr"`
    Content string `xml:"content,attr"`
    Author  string `xml:"author,attr"`
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.