LoginSignup
11
14

More than 5 years have passed since last update.

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

Posted at

前回取得したXMLをパースする部分を書いてみる

encoding/xmlでのパース

encoding/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>

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

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"
のような書き方をすると、
取り出したい値がネストの深い所にある場合、関係ない階層をすっ飛ばして取得することが可能です。

パース処理

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


// 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)
}

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

11
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
14