Go encoding/xml
ライブラリ
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))
}