April 3, 2012

2014 letters 5 mins read

Go言語:templateパッケージでHTML等テキストファイルを読み込む(応用編2)

templateパッケージでHTML等の任意のテキストファイルを読み込み、出力する方法を説明する。 応用編その2ではテンプレート内に埋め込む「アクション」で利用できる様々な機能について解説する。 なお、APIバージョンGo1で説明する。 バージョン3ではパッケージ名、関数名が異なるので注意。

概要

templateパッケージでは、任意のテキストを読み込み、テキストに埋め込まれたタグに対してデータの評価や制御(アクションという)をおこなって出力する。 templateパッケージ応用編1では、アクションの基本的な機能について説明したが、今回は以下のようなさらに高度な機能について説明する。

  • {{if}}アクションにnot、and、or評価をおこなう。
  • 配列、Slice、Mapのサイズや任意のインデックスの値を取得する。
  • 出力結果のフォーマットをおこなう。
  • 任意の演算をおこなった結果を出力する。

not演算によりbool値を評価する

package main

import (
	"fmt"
	"os"
	"text/template"
)

const templateString string = `
{{if not .}}TRUE {{/* not演算をおこなう */}}
{{else}}FALSE
{{end}}
`

func main() {
	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, true); err != nil { // trueを渡す
		fmt.Println(err.Error())
	}
}

実行結果

FALSE

and, or演算によりbool値を評価する

package main

import (
	"fmt"
	"os"
	"text/template"
)

const templateString string = `
{{with .}}
    {{if and .Field1 .Field2}}TRUE {{/* and演算をおこなう */}}
    {{else}}FALSE
    {{end}}
    {{if or .Field1 .Field2}}TRUE {{/* or演算をおこなう */}}
    {{else}}FALSE
    {{end}}
{{end}}
`

type Fields struct {
    Field1 bool
    Field2 bool
}

func main() {
    f := Fields{true, false}

	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, f); err != nil { // 構造体Fieldsを渡す
		fmt.Println(err.Error())
	}
}

実行結果

FALSE
    
TRUE

配列のサイズや任意のインデックスの値を出力する

package main

import (
	"fmt"
	"os"
	"text/template"
)

const templateString string = `
{{len .}} {{/* 配列のサイズを出力する */}}
{{index . 1}} {{/* 配列の[1]の値を出力する */}}
`

func main() {
    array := []int{1, 2, 4, 8}

	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, array); err != nil { // 配列を渡す
		fmt.Println(err.Error())
	}
}

実行結果

4
2

変数に一度値を格納し、出力する

package main

import (
	"fmt"
	"os"
	"text/template"
)

const templateString string = `
{{range $i, $v := .}}{{$i}}:{{$v}}
{{end}}
`

func main() {
    array := []int{1, 2, 4, 8}

	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, array); err != nil { // 配列を渡す
		fmt.Println(err.Error())
	}
}

実行結果

0:1
1:2
2:4
3:8

値のフォーマットを変更して出力する

package main

import (
	"fmt"
	"os"
	"text/template"
)

const templateString string = `
{{.Tags}}

{{html .Tags}} {{/* <や"などhtmlタグに用いる文字列をエスケープする */}}
{{.Tags | html}}

{{printf "%c" .Char}} {{/* 文字型で出力する */}}
{{.Char | printf "%c"}}
`

type Fields struct {
    Tags string
    Char int
}

func main() {
    f := Fields{"<a href="aaa">link</a>", 65}

	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, f); err != nil { // 構造体Fieldsを渡す
		fmt.Println(err.Error())
	}
}

実行結果

<a href="aaa">link</a>

&lt;a href=&#34;aaa&#34;&gt;link&lt;/a&gt;
&lt;a href=&#34;aaa&#34;&gt;link&lt;/a&gt;

A
A

構造体に定義された関数を呼び出した結果を利用する

package main

import (
	"fmt"
	"os"
	"text/template"
)

const templateString string = `
{{if call .F .Field}}TRUE {{/* 構造体に定義されたF関数をFieldフィールドを引数として呼び出す */}}
{{else}}FALSE
{{end}}
`

type Fields struct {
	Field int
    F func(n int) bool 
}

func validate(n int) bool {
    if n > 0 {
		return true
	}
	return false
}

func main() {
    f := Fields{-10, validate}

	var t = template.Must(template.New("text").Parse(templateString))

	if err := t.Execute(os.Stdout, f); err != nil { // 構造体Fieldsを渡す
		fmt.Println(err.Error())
	}
}

実行結果

FALSE

解説

変数

{{.}}{{range .}} {{.}} {{end}}のように直接値を出力するだけでなく、一度変数に格納した値を出力することができる。 任意の値を変数に格納するには以下のように記述する。

$変数名 := 値

配列の値だけでなく、インデックスを一緒に出力したい場合などに使用する。

{{range $i, $x := .}} {{$i}}:{{$x}} {{end}}

関数

  • bool値の評価にnot, and, orを利用できる。
not 引数

引数のbool値をnot演算した結果を返す。

and 引数1 引数2

引数1引数2のbool値をand演算した結果を返す。

or 引数1 引数2

引数1引数2のbool値をor演算した結果を返す。

  • 配列、Slice、Mapのサイズ取得にlen任意のインデックスの値の取得にindexを利用できる。
len 引数

引数のサイズを返す。

index 引数1 引数2

引数1のインデックス[引数2]の値を返す。

  • «や"などhtmlタグに用いる文字列をエスケープするためにhtmlを利用できる。
html 引数
値 | html

どちらも出力結果は同じ。 |により、|の左側で評価された値が右側の関数の第2引数として用いられる。

  • fmtパッケージによるフォーマット変更にprint, printf, printlnを利用できる。
print 引数1 引数2 ...
printf 引数1 引数2 ...
println 引数1 引数2 ...
値 | print 引数1

fmtパッケージの各Print系関数を利用する。 引数1にはフォーマット、引数2以降には出力対象の値を指定する。 使用できるフォーマットはfmtパッケージの各Print系関数と同じ。 例として以下のように使用する。 [text light="true” highlight="1”]{{printf “%c, %d” .Var1 .Var2}}[/text]

  • 構造体や変数に定義された関数を呼び出すためにcallを利用できる。 templateではアクションタグ内で四則演算等、何らかのロジックを実行した結果を出力することができない。 代わりに、templateに渡す変数や構造体に定義した関数を呼び出しその結果を用いることができる。 以下のようにアクションタグを指定する。
call 関数名 (引数1) ...

関数名で指定した関数を引数1に続く引数で呼び出す。 引数がない場合は引数1以降の部分は省略する。 変数や構造体には以下のように関数を定義する。

type FuncStruct struct {
    F func(n int) string
}

func doSomething(n int) string {
    // do something
}

func main() {
    s := FuncStruct{doSomething}
    ...
}

この例で関数doSomethingを呼び出した結果を出力するには以下のようにアクションタグを指定する。

{{call .F 100}}