Go encoding/xml

シリアライゼーションGoXML標準ライブラリ構造化データ

ライブラリ

encoding/xml - Go言語の標準XMLエンコーディング/デコーディングパッケージ

概要

encoding/xmlは、Go言語の標準ライブラリに含まれるXMLシリアライゼーションパッケージです。構造体タグを使用した柔軟なマッピング、名前空間のサポート、ストリーミング処理など、XMLデータの読み書きに必要な機能を包括的に提供します。XMLドキュメントの生成とパース、DOM操作、SAX風のストリーミング処理など、様々なXML処理パターンに対応しており、Webサービスや設定ファイル、データ交換フォーマットとしてのXML処理に適しています。

詳細

encoding/xmlパッケージは、Go言語でXMLを扱うための完全な機能セットを提供します。構造体のフィールドにタグを付けることで、XMLの要素や属性と構造体フィールドのマッピングを宣言的に定義できます。この設計により、複雑なXML構造も直感的にGoの型システムにマッピングできます。

パッケージは、Marshal/Unmarshal関数による一括処理と、Encoder/Decoderによるストリーミング処理の両方をサポートしています。大きなXMLファイルを扱う場合は、Decoderのトークンベースの処理により、メモリ効率的な処理が可能です。また、xml.Marshalerやxml.Unmarshalerインターフェースを実装することで、カスタムシリアライゼーションロジックを定義できます。

XML名前空間、CDATA、コメント、処理命令などのXML仕様の重要な機能もサポートしています。ただし、DTD検証やXSDスキーマ検証などの高度な機能は含まれていないため、そのような機能が必要な場合は外部ライブラリを使用する必要があります。

メリット・デメリット

メリット

  • 標準ライブラリ: 外部依存なしでXML処理が可能
  • 柔軟なマッピング: 構造体タグによる宣言的なXML-Go型マッピング
  • ストリーミング対応: 大きなXMLファイルの効率的な処理
  • 名前空間サポート: XML名前空間の適切な処理
  • カスタマイズ可能: Marshaler/Unmarshalerインターフェースによる拡張
  • 型安全: コンパイル時の型チェックによる安全性

デメリット

  • スキーマ検証なし: DTDやXSDによる検証は未サポート
  • XPath未対応: XPath式によるクエリ機能なし
  • XSLT未対応: XSL変換機能なし
  • パフォーマンス: 特化型XMLライブラリより低速な場合がある
  • エラーメッセージ: XMLパースエラーのメッセージが分かりにくいことがある
  • 完全性: 一部のXML仕様(外部エンティティ等)は未対応

参考ページ

書き方の例

1. 基本的なXMLエンコード・デコード

package main

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

type Person struct {
    XMLName xml.Name `xml:"person"`
    Name    string   `xml:"name"`
    Age     int      `xml:"age,attr"`
    Email   string   `xml:"contact>email"`
    Phone   string   `xml:"contact>phone"`
}

func main() {
    // エンコード
    person := Person{
        Name:  "田中太郎",
        Age:   30,
        Email: "[email protected]",
        Phone: "090-1234-5678",
    }
    
    xmlData, err := xml.MarshalIndent(person, "", "  ")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("XML出力:")
    fmt.Println(xml.Header + string(xmlData))
    
    // デコード
    var decoded Person
    err = xml.Unmarshal(xmlData, &decoded)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("\nデコード結果: %+v\n", decoded)
}

2. 複雑な構造とリストの処理

package main

import (
    "encoding/xml"
    "fmt"
)

type Library struct {
    XMLName xml.Name `xml:"library"`
    Name    string   `xml:"name,attr"`
    Books   []Book   `xml:"books>book"`
}

type Book struct {
    ISBN      string   `xml:"isbn,attr"`
    Title     string   `xml:"title"`
    Authors   []string `xml:"authors>author"`
    Publisher string   `xml:"publisher"`
    Year      int      `xml:"year"`
    Price     float64  `xml:"price"`
    Currency  string   `xml:"price,attr,currency"`
}

func complexStructureExample() {
    library := Library{
        Name: "技術書図書館",
        Books: []Book{
            {
                ISBN:      "978-4-12345-678-9",
                Title:     "Go言語プログラミング",
                Authors:   []string{"山田太郎", "鈴木花子"},
                Publisher: "技術出版社",
                Year:      2023,
                Price:     3500,
                Currency:  "JPY",
            },
            {
                ISBN:      "978-4-98765-432-1",
                Title:     "実践XMLデータ処理",
                Authors:   []string{"佐藤一郎"},
                Publisher: "データ出版",
                Year:      2022,
                Price:     2800,
                Currency:  "JPY",
            },
        },
    }
    
    xmlData, _ := xml.MarshalIndent(library, "", "  ")
    fmt.Println(xml.Header + string(xmlData))
}

3. 名前空間の処理

package main

import (
    "encoding/xml"
    "fmt"
)

type Envelope struct {
    XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
    Body    Body     `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
}

type Body struct {
    GetWeather GetWeatherRequest `xml:"http://weather.example.com GetWeather"`
}

type GetWeatherRequest struct {
    City        string `xml:"city"`
    CountryCode string `xml:"countryCode"`
}

func namespaceExample() {
    request := Envelope{
        Body: Body{
            GetWeather: GetWeatherRequest{
                City:        "Tokyo",
                CountryCode: "JP",
            },
        },
    }
    
    xmlData, _ := xml.MarshalIndent(request, "", "  ")
    fmt.Println(string(xmlData))
}

4. ストリーミング処理(大きなXMLファイル)

package main

import (
    "encoding/xml"
    "fmt"
    "io"
    "strings"
)

func streamingExample() {
    xmlData := `
    <products>
        <product id="1"><name>製品A</name><price>1000</price></product>
        <product id="2"><name>製品B</name><price>2000</price></product>
        <product id="3"><name>製品C</name><price>3000</price></product>
    </products>`
    
    decoder := xml.NewDecoder(strings.NewReader(xmlData))
    
    var inProduct bool
    var currentProduct struct {
        ID    string
        Name  string
        Price string
    }
    
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Printf("エラー: %v\n", err)
            return
        }
        
        switch t := token.(type) {
        case xml.StartElement:
            if t.Name.Local == "product" {
                inProduct = true
                for _, attr := range t.Attr {
                    if attr.Name.Local == "id" {
                        currentProduct.ID = attr.Value
                    }
                }
            } else if inProduct {
                var value string
                decoder.DecodeElement(&value, &t)
                switch t.Name.Local {
                case "name":
                    currentProduct.Name = value
                case "price":
                    currentProduct.Price = value
                }
            }
        case xml.EndElement:
            if t.Name.Local == "product" {
                fmt.Printf("製品: ID=%s, 名前=%s, 価格=%s\n",
                    currentProduct.ID, currentProduct.Name, currentProduct.Price)
                currentProduct = struct{ ID, Name, Price string }{}
                inProduct = false
            }
        }
    }
}

5. カスタムマーシャリング

package main

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

type DateTime struct {
    time.Time
}

// MarshalXML implements xml.Marshaler
func (dt DateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
    dateStr := dt.Format("2006-01-02T15:04:05")
    return e.EncodeElement(dateStr, start)
}

// UnmarshalXML implements xml.Unmarshaler
func (dt *DateTime) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    var dateStr string
    if err := d.DecodeElement(&dateStr, &start); err != nil {
        return err
    }
    
    parsed, err := time.Parse("2006-01-02T15:04:05", dateStr)
    if err != nil {
        return err
    }
    
    dt.Time = parsed
    return nil
}

type Event struct {
    XMLName   xml.Name `xml:"event"`
    Name      string   `xml:"name"`
    StartTime DateTime `xml:"startTime"`
    EndTime   DateTime `xml:"endTime"`
}

func customMarshalingExample() {
    event := Event{
        Name:      "技術カンファレンス",
        StartTime: DateTime{time.Now()},
        EndTime:   DateTime{time.Now().Add(2 * time.Hour)},
    }
    
    xmlData, _ := xml.MarshalIndent(event, "", "  ")
    fmt.Println(string(xmlData))
    
    // デコードテスト
    var decoded Event
    xml.Unmarshal(xmlData, &decoded)
    fmt.Printf("イベント: %s\n開始: %v\n終了: %v\n",
        decoded.Name,
        decoded.StartTime.Format("2006/01/02 15:04"),
        decoded.EndTime.Format("2006/01/02 15:04"))
}

6. XMLとJSONの相互変換

package main

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

type Product struct {
    XMLName     xml.Name `xml:"product" json:"-"`
    ID          int      `xml:"id,attr" json:"id"`
    Name        string   `xml:"name" json:"name"`
    Description string   `xml:"description" json:"description"`
    Price       float64  `xml:"price" json:"price"`
    InStock     bool     `xml:"inStock" json:"in_stock"`
}

func xmlJsonConversion() {
    // 元データ
    product := Product{
        ID:          123,
        Name:        "高性能ノートPC",
        Description: "最新のCPUとGPUを搭載",
        Price:       150000,
        InStock:     true,
    }
    
    // XMLに変換
    xmlData, _ := xml.MarshalIndent(product, "", "  ")
    fmt.Println("XML形式:")
    fmt.Println(string(xmlData))
    
    // JSONに変換
    jsonData, _ := json.MarshalIndent(product, "", "  ")
    fmt.Println("\nJSON形式:")
    fmt.Println(string(jsonData))
    
    // XMLからJSONへの変換
    var fromXML Product
    xml.Unmarshal(xmlData, &fromXML)
    jsonFromXML, _ := json.MarshalIndent(fromXML, "", "  ")
    fmt.Println("\nXML→JSON変換結果:")
    fmt.Println(string(jsonFromXML))
}