Cybozu ガルーン API のレスポンスのXMLを golang でパースする


[前回](http://qiita.com/yamasaki-masahide/items/03dfa6cd70ff20607b58)取得したXMLをパースする部分を書いてみる


encoding/xmlでのパース
--------------------

encoding/xmlでは、XMLの構造に一致するように構造体を作ると簡単にパースできるようです

レスポンスのXMLは以下のような感じ

```xml:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
 xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns:schedule="http://wsdl.cybozu.co.jp/schedule/2008">
 <soap:Header>・・・・・</soap:Header>
 <soap:Body>
  <schedule:ScheduleGetEventsResponse>
   <returns>
    <schedule_event id="1351459" event_type="repeat" public_type="public" plan="プラン名" detail="タイトル" version="1387216782" timezone="Asia/Tokyo"  allday="false" start_only="false">
     <members xmlns="http://schemas.cybozu.co.jp/schedule/2008">
      <member>
       <user id="1111" name="名前" order="0"/>
      </member>
     </members>
     <repeat_info xmlns="http://schemas.cybozu.co.jp/schedule/2008">
      <condition type="weekday" day="1"
       week="3" start_date="2014-01-01" end_date="2014-01-31"
       start_time="10:00:00" end_time="10:30:00"/>
      <exclusive_datetimes>
      </exclusive_datetimes>
     </repeat_info>
     </schedule_event>
   </returns>
  </schedule:ScheduleGetEventsResponse>
 </soap:Body>
</soap:Envelope>
```

こちらをパースするための構造体を定義

```go:
type XMLexclusive struct {
        XMLName xml.Name `xml:"exclusive_datetime"`
        Start   string   `xml:"start,attr"`
        End     string   `xml:"end,attr"`
}
type XMLrepeat_condition struct {
        XMLName   xml.Name `xml:"condition"`
        Type      string   `xml:"type,attr"`
        Day       string   `xml:"day,attr"`
        Week      string   `xml:"week,attr"`
        StartDate string   `xml:"start_date,attr"`
        EndDate   string   `xml:"end_date,attr"`
        StartTime string   `xml:"start_time,attr"`
        EndTime   string   `xml:"end_time,attr"`
}
type XMLrepeat struct {
        XMLName   xml.Name             `xml:"repeat_info"`
        Condition *XMLrepeat_condition `xml:"condition"`
        Exclusive []*XMLexclusive      `xml:"exclusive_datetimes>exclusive_datetime"`
}

type XMLdatetime struct {
        XMLName xml.Name `xml:"datetime"`
        Start   string   `xml:"start,attr"`
        End     string   `xml:"end,attr"`
}
type XMLschedule_event struct {
        XMLName    xml.Name     `xml:"schedule_event"`
        Id         int          `xml:"id,attr"`
        EventType  string       `xml:"event_type,attr"`
        PublicType string       `xml:"public_type,attr"`
        Plan       string       `xml:"plan,attr"`
        Detail     string       `xml:"detail,attr"`
        TimeZone   string       `xml:"timezone,attr"`
        Version    int          `xml:"version,attr"`
        Allday     bool         `xml:"allday,attr"`
        StartOnly  bool         `xml:"start_only,attr"`
        Datetime   *XMLdatetime `xml:"when>datetime"`
        Repeat     *XMLrepeat   `xml:"repeat_info"`
}
type XMLSoap struct {
        XMLName        xml.Name             `xml:"Envelope"`
        Schedule_event []*XMLschedule_event `xml:"Body>ScheduleGetEventsResponse>returns>schedule_event"`  // 「>」で接続することでelementツリーをその順にたどってくれるようです
}

```

こんな感じで。 
ちょっと構造をネストで定義していくのが面倒ですが、
取得する必要の無いエレメントやアトリビュート等は記述しなくてもよいので楽です。
また、上記のように変数に対応するエレメントを表す文字列に、`xml:"Body>ScheduleGetEventsResponse>returns>schedule_event"`
のような書き方をすると、
取り出したい値がネストの深い所にある場合、関係ない階層をすっ飛ばして取得することが可能です。


パース処理
--------

あとは定義した構造体をパーサーに渡すだけです。

```go:

// Soapレスポンスをパースする
func ReadSoap(reader io.Reader) ([]*XMLschedule_event, error) {
        xmlSoap := &XMLSoap{}
        decoder := xml.NewDecoder(reader)

        if err := decoder.Decode(xmlSoap); err != nil {
                return nil, err
        }

        return xmlSoap.Schedule_event, nil
}

func main() {
        var xmlSoapBody []*XMLschedule_event
        var file *os.File

        defer func() {
                if file != nil {
                        file.Close()
                }
        }()
        strapsFilePath, err := filepath.Abs("test_schedule_event.xml")
        if err != nil {
                panic(err.Error())
        }
        file, err = os.Open(strapsFilePath)
        if err != nil {
                panic(err.Error())
        }
        xmlSoapBody, err = ReadSoap(file)
        if err != nil {
                panic(err.Error())
        }
        pretty.Printf("xmlSoapBody:%# v\n", xmlSoapBody)
}
```

これで必要な情報のデコードができました。