Home > Back-end >  How to getting an empty array when parsing XML?
How to getting an empty array when parsing XML?

Time:12-14

I was tasked with writing a Go utility that takes an XML file, parses it, and returns it in JSON.

Here is an example of the XML:

<?xml version="1.0" encoding="utf-8"?>
<tracks clid="020">
    <track uuid="551" category="s" route="8" vehicle_type="trolleybus">
        <point latitude="53.61491" longitude="55.90922" avg_speed="24" direction="270" time="13122022:072116"/>
    </track>
    <track uuid="552" category="s" route="6" vehicle_type="trolleybus">
        <point latitude="53.68321" longitude="57.90922" avg_speed="42" direction="181" time="13122022:072216"/>
    </track>
</tracks>

I wrote the following code:

package main

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
)

type Tracks struct {
    XMLName xml.Name `xml:"tracks" json:"-"`
    Clid    string   `xml:"clid,attr" json:"clid"`
    Tracks  []Track  `xml:"track" json:"track_list"`
}

type Track struct {
    XMLName     xml.Name `xml:"tracks"`
    Uuid        string   `xml:"uuid,attr" json:"uuid"`
    Category    string   `xml:"category,attr" json:"category"`
    Route       string   `xml:"route,attr" json:"route"`
    VehicleType string   `xml:"vehicle_type,attr" json:"vehicle_type"`
    Point       Point    `xml:"point" json:"point"`
}

type Point struct {
    Latitude  string `xml:"latitude,attr" json:"latitude"`
    Longitude string `xml:"longitude,attr" json:"longitude"`
    AvgSpeed  string `xml:"avg_speed,attr" json:"avg_speed"`
    Direction string `xml:"direction,attr" json:"direction"`
    Time      string `xml:"time,attr" json:"time"`
}

func main() {
    rawXmlData := `
        <?xml version="1.0" encoding="utf-8"?>
        <tracks clid="020">
            <track uuid="551" category="s" route="8" vehicle_type="trolleybus">
                <point latitude="53.61491" longitude="55.90922" avg_speed="24" direction="270" time="13122022:072116"/>
            </track>
            <track uuid="552" category="s" route="6" vehicle_type="trolleybus">
                <point latitude="53.68321" longitude="57.90922" avg_speed="42" direction="181" time="13122022:072216"/>
            </track>
        </tracks>
    `

    var tracks Tracks

    err := xml.Unmarshal([]byte(rawXmlData), &tracks)
    if err != nil {
        log.Fatal(err)
    }

    jsonData, err := json.Marshal(tracks)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf(string(jsonData))
}

Go.dev

But, unfortunately, it doesn't work. I get the following in the console:

2009/11/10 23:00:00 expected element type <tracks> but have <track>

What am I doing wrong? How can I solve this problem?

CodePudding user response:

I thought I would move the discussion to an answer, since I think you're pretty close. As I mentioned, you need to check the error returned by xml.Unmarshal. That might look like this:

    if err := xml.Unmarshal([]byte(rawXmlData), &tracks); err != nil {
        panic(err)
    }

Now that you have valid XML data in your code, we can produce meaningful errors; with the above error check in place, running your code produces:

panic: expected element type <tracks> but have <track>

goroutine 1 [running]:
main.main()
        /home/lars/tmp/go/main.go:48  0x12f

That's happening because of a minor typo in your data structures; in the definition of your Track struct, you have:

type Track struct {
    XMLName     xml.Name `xml:"tracks"`
    Uuid        string   `xml:"uuid,attr" json:"uuid"`
    Category    string   `xml:"category,attr" json:"category"`
    Route       string   `xml:"route,attr" json:"route"`
    VehicleType string   `xml:"vehicle_type,attr" json:"vehicle_type"`
    Point       Point    `xml:"point" json:"point"`
}

You've mis-tagged the XMLName attribute as tracks when it should be track:

type Track struct {
    XMLName     xml.Name `xml:"track"`
    Uuid        string   `xml:"uuid,attr" json:"uuid"`
    Category    string   `xml:"category,attr" json:"category"`
    Route       string   `xml:"route,attr" json:"route"`
    VehicleType string   `xml:"vehicle_type,attr" json:"vehicle_type"`
    Point       Point    `xml:"point" json:"point"`
}

Lastly -- and this isn't directly related to the problem -- you should avoid naming a variable error, because that's the name of the built-in data type of errors. I would modify your call to json.Marshal like this:

    jsonData, err := json.Marshal(tracks)
    if err != nil {
        panic(err)
    }

You don't need to panic() on errors; this is just a convenient way to bail out of the code.


With these changes in place, if we compile and run the code we get as output (formatted with jq):

{
  "clid": "020",
  "track_list": [
    {
      "XMLName": {
        "Space": "",
        "Local": "track"
      },
      "uuid": "551",
      "category": "s",
      "route": "8",
      "vehicle_type": "trolleybus",
      "point": {
        "latitude": "53.61491",
        "longitude": "55.90922",
        "avg_speed": "24",
        "direction": "270",
        "time": "13122022:072116"
      }
    },
    {
      "XMLName": {
        "Space": "",
        "Local": "track"
      },
      "uuid": "552",
      "category": "s",
      "route": "6",
      "vehicle_type": "trolleybus",
      "point": {
        "latitude": "53.68321",
        "longitude": "57.90922",
        "avg_speed": "42",
        "direction": "181",
        "time": "13122022:072216"
      }
    }
  ]
}

Note that you don't even need that XMLName element in your structure; if we remove it completely so that we have:

type Track struct {
    Uuid        string `xml:"uuid,attr" json:"uuid"`
    Category    string `xml:"category,attr" json:"category"`
    Route       string `xml:"route,attr" json:"route"`
    VehicleType string `xml:"vehicle_type,attr" json:"vehicle_type"`
    Point       Point  `xml:"point" json:"point"`
}

Then we get as output (formatted with jq):

{
  "clid": "020",
  "track_list": [
    {
      "uuid": "551",
      "category": "s",
      "route": "8",
      "vehicle_type": "trolleybus",
      "point": {
        "latitude": "53.61491",
        "longitude": "55.90922",
        "avg_speed": "24",
        "direction": "270",
        "time": "13122022:072116"
      }
    },
    {
      "uuid": "552",
      "category": "s",
      "route": "6",
      "vehicle_type": "trolleybus",
      "point": {
        "latitude": "53.68321",
        "longitude": "57.90922",
        "avg_speed": "42",
        "direction": "181",
        "time": "13122022:072216"
      }
    }
  ]
}

CodePudding user response:

type Track struct {
    XMLName     xml.Name `xml:"tracks"`

should be "track" not "tracks"

https://go.dev/play/p/kg-Dyep8Fv9

  • Related