いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-481 (etree)(PythonのElementTreeに似たXML操作ライブラリ)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

XMLをちょっと扱う作業があって、なんかいいライブラリないかなーって探してたら以下を発見。

github.com

PythonのElementTreeライブラリにインスパイされて作成されたライブラリと記載されている通り、直感的な使い方が出来るので気に入りました。

てことで、基本的な使い方のサンプルを以下にメモメモ。。。

サンプル

書き込み

main.go
package main

import (
    "context"
    "os"

    "github.com/beevik/etree"
)

var (
    langs = map[string]string{
        "golang": "https://go.dev/",
        "csharp": "https://learn.microsoft.com/ja-jp/dotnet/csharp/",
        "java":   "https://docs.oracle.com/javase/jp/21/index.html",
        "python": "https://docs.python.org/ja/3/",
        "rust":   "https://www.rust-lang.org/ja/learn",
    }
)

func main() {
    var (
        rootCtx  = context.Background()
        ctx, cxl = context.WithCancel(rootCtx)
    )
    defer cxl()

    if err := run(ctx); err != nil {
        panic(err)
    }
}

func run(_ context.Context) error {
    //
    // etreeライブラリは、PythonのElementTreeライブラリに
    // インスパイアされて作成されているXML処理ライブラリ。
    //
    // 使いやすいAPIが備わっているので直感的に作業できる。
    //

    var (
        doc       *etree.Document
        languages *etree.Element
        language  *etree.Element
    )
    doc = etree.NewDocument()
    doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`)

    languages = doc.CreateElement("languages")
    for lang, url := range langs {
        language = languages.CreateElement("language")
        language.CreateAttr("name", lang)
        language.SetText(url)
    }

    doc.Indent(4)
    if _, err := doc.WriteTo(os.Stdout); err != nil {
        return err
    }

    return nil
}

実行すると以下のように出力されます。

Taskfile.yml
# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - go run .
shell
$ task
task: [default] go run .
<?xml version="1.0" encoding="UTF-8"?>
<languages>
    <language name="rust">https://www.rust-lang.org/ja/learn</language>
    <language name="golang">https://go.dev/</language>
    <language name="csharp">https://learn.microsoft.com/ja-jp/dotnet/csharp/</language>
    <language name="java">https://docs.oracle.com/javase/jp/21/index.html</language>
    <language name="python">https://docs.python.org/ja/3/</language>
</languages>

try-golang-extlib/examples/etree/write at main · devlights/try-golang-extlib · GitHub

読み込み

main.go
package main

import (
    "context"
    "log/slog"
    "os"

    "github.com/beevik/etree"
)

var (
    logger *slog.Logger
)

func init() {
    var (
        opt = &slog.HandlerOptions{
            ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
                switch {
                case a.Key == slog.LevelKey:
                    return slog.Attr{}
                case a.Key == slog.TimeKey:
                    return slog.Attr{}
                case a.Key == slog.MessageKey:
                    return slog.Attr{Key: "tag", Value: a.Value}
                default:
                    return a
                }
            },
        }
        handler = slog.NewJSONHandler(os.Stdout, opt)
    )

    logger = slog.New(handler)
}

func main() {
    var (
        rootCtx  = context.Background()
        ctx, cxl = context.WithCancel(rootCtx)
    )
    defer cxl()

    if err := run(ctx); err != nil {
        panic(err)
    }
}

func run(_ context.Context) error {
    //
    // etreeライブラリは、PythonのElementTreeライブラリに
    // インスパイアされて作成されているXML処理ライブラリ。
    //
    // 使いやすいAPIが備わっているので直感的に作業できる。
    //

    var (
        doc *etree.Document
        err error
    )
    doc = etree.NewDocument()
    if err = doc.ReadFromFile("langs.xml"); err != nil {
        return err
    }

    var (
        root      = doc.SelectElement("languages")
        languages = root.SelectElements("language")
    )
    for _, lang := range languages {
        var (
            attrs = make([]any, 0)
        )

        for _, a := range lang.Attr {
            attrs = append(attrs, slog.Attr{Key: a.Key, Value: slog.StringValue(a.Value)})
        }

        attrs = append(attrs, slog.Attr{Key: "text", Value: slog.StringValue(lang.Text())})

        logger.Info(lang.Tag, attrs...)
    }

    return nil
}

実行すると、以下のようになります。前述した書き込みのサンプルで出力したXMLを出力して読み込むようにしています。

Taskfile.yml
# https://taskfile.dev

version: '3'

vars:
  XML_FILE: langs.xml

tasks:
  default:
    cmds:
      - task: gen
      - defer: rm -f {{.XML_FILE}}
      - go run .
  gen:
    dir: ../write
    cmds:
      - go run . > ../read/{{.XML_FILE}}
shell
$ task
task: [gen] go run . > ../read/langs.xml
task: [default] go run .
{"tag":"language","name":"golang","text":"https://go.dev/"}
{"tag":"language","name":"csharp","text":"https://learn.microsoft.com/ja-jp/dotnet/csharp/"}
{"tag":"language","name":"java","text":"https://docs.oracle.com/javase/jp/21/index.html"}
{"tag":"language","name":"python","text":"https://docs.python.org/ja/3/"}
{"tag":"language","name":"rust","text":"https://www.rust-lang.org/ja/learn"}
task: [default] rm -f langs.xml

try-golang-extlib/examples/etree/read at main · devlights/try-golang-extlib · GitHub

参考情報

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。