Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

前回取得した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)
}

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

yamasaki-masahide
インフラ関連技術に興味があります。 暇があればgolangで何か作りたい
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした