diff --git a/ch7/ch7-13.md b/ch7/ch7-13.md
index 7ba9048..6f8aaac 100644
--- a/ch7/ch7-13.md
+++ b/ch7/ch7-13.md
@@ -1,3 +1,82 @@
-## 7.13. 類型分支
-
-TODO
+## 7.13. 類型開關
+接口被以兩種不同的方式使用。在第一個方式中,以io.Reader,io.Writer,fmt.Stringer,sort.Interface,http.Handler,和error爲典型,一個接口的方法表達了實現這個接口的具體類型間的相思性,但是隱藏了代表的細節和這些具體類型本身的操作。重點在於方法上,而不是具體的類型上。
+
+第二個方式利用一個接口值可以持有各種具體類型值的能力併且將這個接口認爲是這些類型的union(聯合)。類型斷言用來動態地區别這些類型併且對每一種情況都不一樣。在這個方式中,重點在於具體的類型滿足這個接口,而不是在於接口的方法(如果它確實有一些的話),併且沒有任何的信息隱藏。我們將以這種方式使用的接口描述爲discriminated unions(可辨識聯合)。
+
+如果你熟悉面向對象編程,你可能會將這兩種方式當作是subtype polymorphism(子類型多態)和 ad hoc polymorphism(非參數多態),但是你不需要去記住這些術語。對於本章剩下的部分,我們將會呈現一些第二種方式的例子。
+
+和其它那些語言一樣,Go語言査詢一個SQL數據庫的API會榦淨地將査詢中固定的部分和變化的部分分開。一個調用的例子可能看起來像這樣:
+```go
+import "database/sql"
+
+func listTracks(db sql.DB, artist string, minYear, maxYear int) {
+ result, err := db.Exec(
+ "SELECT * FROM tracks WHERE artist = ? AND ? <= year AND year <= ?",
+ artist, minYear, maxYear)
+ // ...
+}
+```
+Exec方法使用SQL字面量替換在査詢字符串中的每個'?';SQL字面量表示相應參數的值,它有可能是一個布爾值,一個數字,一個字符串,或者nil空值。用這種方式構造査詢可以幫助避免SQL註入攻擊;這種攻擊就是對手可以通過利用輸入內容中不正確的引文來控製査詢語句。在Exec函數內部,我們可能會找到像下面這樣的一個函數,它會將每一個參數值轉換成它的SQL字面量符號。
+```go
+func sqlQuote(x interface{}) string {
+ if x == nil {
+ return "NULL"
+ } else if _, ok := x.(int); ok {
+ return fmt.Sprintf("%d", x)
+ } else if _, ok := x.(uint); ok {
+ return fmt.Sprintf("%d", x)
+ } else if b, ok := x.(bool); ok {
+ if b {
+ return "TRUE"
+ }
+ return "FALSE"
+ } else if s, ok := x.(string); ok {
+ return sqlQuoteString(s) // (not shown)
+ } else {
+ panic(fmt.Sprintf("unexpected type %T: %v", x, x))
+ }
+}
+```
+switch語句可以簡化if-else鏈,如果這個if-else鏈對一連串值做相等測試。一個相似的type switch(類型開關)可以簡化類型斷言的if-else鏈。
+
+在它最簡單的形式中,一個類型開關像普通的switch語句一樣,它的運算對象是x.(type)-它使用了關鍵詞字面量type-併且每個case有一到多個類型。一個類型開關基於這個接口值的動態類型使一個多路分支有效。這個nil的case和if x == nil匹配,併且這個default的case和如果其它case都不匹配的情況匹配。一個對sqlQuote的類型開關可能會有這些case:
+```go
+switch x.(type) {
+ case nil: // ...
+ case int, uint: // ...
+ case bool: // ...
+ case string: // ...
+ default: // ...
+}
+```
+和(§1.8)中的普通switch語句一樣,每一個case會被順序的進行考慮,併且當一個匹配找到時,這個case中的內容會被執行。當一個或多個case類型是接口時,case的順序就會變得很重要,因爲可能會有兩個case同時匹配的情況。default case相對其它case的位置是無所謂的。它不會允許落空發生。
+
+註意到在原來的函數中,對於bool和string情況的邏輯需要通過類型斷言訪問提取的值。因爲這個做法很典型,類型開關語句有一個擴展的形式,它可以將提取的值綁定到一個在每個case范圍內的新變量。
+```go
+switch x := x.(type) { /* ... */ }
+```
+這里我們已經將新的變量也命名爲x;和類型斷言一樣,重用變量名是很常見的。和一個switch語句相似地,一個類型開關隱式的創建了一個語言塊,因此新變量x的定義不會和外面塊中的x變量衝突。每一個case也會隱式的創建一個單獨的語言塊。
+
+使用類型開關的擴展形式來重寫sqlQuote函數會讓這個函數更加的清晰:
+```go
+func sqlQuote(x interface{}) string {
+ switch x := x.(type) {
+ case nil:
+ return "NULL"
+ case int, uint:
+ return fmt.Sprintf("%d", x) // x has type interface{} here.
+ case bool:
+ if x {
+ return "TRUE"
+ }
+ return "FALSE"
+ case string:
+ return sqlQuoteString(x) // (not shown)
+ default:
+ panic(fmt.Sprintf("unexpected type %T: %v", x, x))
+ }
+}
+```
+在這個版本的函數中,在每個單一類型的case內部,變量x和這個case的類型相同。例如,變量x在bool的case中是bool類型和string的case中是string類型。在所有其它的情況中,變量x是switch運算對象的類型(接口);在這個例子中運算對象是一個interface{}。當多個case需要相同的操作時,比如int和uint的情況,類型開關可以很容易的合併這些情況。
+
+盡管sqlQuote接受一個任意類型的參數,但是這個函數隻會在它的參數匹配類型開關中的一個case時運行到結束;其它情況的它會panic出“unexpected type”消息。雖然x的類型是interface{},但是我們把它認爲是一個int,uint,bool,string,和nil值的discriminated union(可識别聯合)
diff --git a/ch7/ch7-14.md b/ch7/ch7-14.md
index d8121df..aaddeb8 100644
--- a/ch7/ch7-14.md
+++ b/ch7/ch7-14.md
@@ -1,3 +1,121 @@
## 7.14. 示例: 基於標記的XML解碼
+第4.5章節展示了如何使用encoding/json包中的Marshal和Unmarshal函數來將JSON文檔轉換成Go語言的數據結構。encoding/xml包提供了一個相似的API。當我們想構造一個文檔樹的表示時使用encoding/xml包會很方便,但是對於很多程序併不是必須的。encoding/xml包也提供了一個更低層的基於標記的API用於XML解碼。在基於標記的樣式中,解析器消費輸入和産生一個標記流;四個主要的標記類型-StartElement,EndElement,CharData,和Comment-每一個都是encoding/xml包中的具體類型。每一個對(\*xml.Decoder).Token的調用都返迴一個標記。
-TODO
+這里顯示的是和這個API相關的部分:
+```go
+// encoding/xml
+package xml
+
+type Name struct {
+ Local string // e.g., "Title" or "id"
+}
+
+type Attr struct { // e.g., name="value"
+ Name Name
+ Value string
+}
+
+// A Token includes StartElement, EndElement, CharData,
+// and Comment, plus a few esoteric types (not shown).
+type Token interface{}
+type StartElement struct { // e.g.,
CharData
+type Comment []byte // e.g., + +type Decoder struct{ /* ... */ } +func NewDecoder(io.Reader) *Decoder +func (*Decoder) Token() (Token, error) // returns next Token in sequence +``` +這個沒有方法的Token接口也是一個可識别聯合的例子。傳統的接口如io.Reader的目的是隱藏滿足它的具體類型的細節,這樣就可以創造出新的實現;在這個實現中每個具體類型都被統一地對待。相反,滿足可識别聯合的具體類型的集合被設計確定和暴露,而不是隱藏。可識别的聯合類型幾乎沒有方法;操作它們的函數使用一個類型開關的case集合來進行表述;這個case集合中每一個case中有不同的邏輯。 + +下面的xmlselect程序獲取和打印在一個XML文檔樹中確定的元素下找到的文本。使用上面的API,它可以在輸入上一次完成它的工作而從來不要具體化這個文檔樹。 +```go +// gopl.io/ch7/xmlselect +// Xmlselect prints the text of selected elements of an XML document. +package main + +import ( + "encoding/xml" + "fmt" + "io" + "os" + "strings" +) + +func main() { + dec := xml.NewDecoder(os.Stdin) + var stack []string // stack of element names + for { + tok, err := dec.Token() + if err == io.EOF { + break + } else if err != nil { + fmt.Fprintf(os.Stderr, "xmlselect: %v\n", err) + os.Exit(1) + } + switch tok := tok.(type) { + case xml.StartElement: + stack = append(stack, tok.Name.Local) // push + case xml.EndElement: + stack = stack[:len(stack)-1] // pop + case xml.CharData: + if containsAll(stack, os.Args[1:]) { + fmt.Printf("%s: %s\n", strings.Join(stack, " "), tok) + } + } + } +} + +// containsAll reports whether x contains the elements of y, in order. +func containsAll(x, y []string) bool { + for len(y) <= len(x) { + if len(y) == 0 { + return true + } + if x[0] == y[0] { + y = y[1:] + } + x = x[1:] + } + return false +} +``` +每次main函數中的循環遇到一個StartElement時,它把這個元素的名稱壓到一個棧里;併且每次遇到EndElement時,它將名稱從這個棧中推出。這個API保證了StartElement和EndElement的序列可以被完全的匹配,甚至在一個糟糕的文檔格式中。註釋會被忽略。當xmlselect遇到一個CharData時,隻有當棧中有序地包含所有通過命令行參數傳入的元素名稱時它才會輸出相應的文本。 + +下面的命令打印出任意出現在兩層div元素下的h2元素的文本。它的輸入是XML的説明文檔,併且它自己就是XML文檔格式的。 +``` +$ go build gopl.io/ch1/fetch +$ ./fetch http://www.w3.org/TR/2006/REC-xml11-20060816 | + ./xmlselect div div h2 +html body div div h2: 1 Introduction +html body div div h2: 2 Documents +html body div div h2: 3 Logical Structures +html body div div h2: 4 Physical Structures +html body div div h2: 5 Conformance +html body div div h2: 6 Notation +html body div div h2: A References +html body div div h2: B Definitions for Character Normalization +... +``` +練習7.17:擴展xmlselect程序以便讓元素不僅僅可以通過名稱選擇,也可以通過它們CSS樣式上屬性進行選擇;例如一個像這樣