2017-10-16 80 views
2
package main 

import (
    "fmt" 
    "encoding/json" 
) 

type Ticket struct { 
    From string 
    To string 
} 

func (t Ticket) String() string { 
    return fmt.Sprintf("%s - %s", t.From, t.To) 
} 

type Passenger struct { 
    Name string `json:"Name"` 
    Tkt Ticket `json:"Ticket"` 
} 

func main() { 
    p := Passenger{} 
    p.Name = "John" 
    p.Tkt.From = "New York" 
    p.Tkt.To = "Washington" 

    buf, _ := json.Marshal(p) 
    fmt.Println(string(buf)) 
} 

此代碼輸出:如何正確使用String()方法將嵌入式結構序列化爲JSON字符串?

{"Name":"John","Ticket":{"From":"New York","To":"Washington"}} 

但是,使用json.Marshal()方法(方法很簡單,友好的複雜結構),如何使它的輸出是這樣的:

{"Name":"John","Ticket":"New York - Washington"} 

回答

3

要生成的JSON Go值的表示,encoding/json包檢查值是否實現了json.Marshalerencoding.TextMarshaler接口,如果是,則使用/調用它們(按此順序)。這記錄在json.Marshal()

元帥遞歸地遍歷值v。如果遇到的值實現了Marshaler接口並且不是零指針,則Marshal會調用其MarshalJSON方法生成JSON。如果沒有MarshalJSON方法存在,但該值實現的是encoding.TextMarshaler,Marshal會調用它的MarshalText方法並將結果編碼爲JSON字符串。

json/encoding包不關心String()方法。所以,如果你想控制你的值(Ticket結構)的JSON表示/輸出,實現它​​json.Marshaler(其中你可以叫String()到您喜歡):

func (t Ticket) MarshalJSON() ([]byte, error) { 
    return []byte(`"` + t.String() + `"`), nil 
} 

那麼輸出會隨着你慾望:

{"Name":"John","Ticket":"New York - Washington"} 

Go Playground上試試。

有一點要注意的事項:如果Ticket.String()產生的string將包含引號",輸出會變得無效JSON,或者更可能json.Marshal()將返回一個錯誤。

採取這樣逃逸的護理,最好/最簡單的就是使用json包本身:它告訴JSON編碼的Ticket.String()string結果:

func (t Ticket) MarshalJSON() ([]byte, error) { 
    return json.Marshal(t.String()) 
} 

現在,如果我們測試它像這樣:

p.Name = "John" 
p.Tkt.From = "New\" York" // CONTAINS QUOTE 
p.Tkt.To = "Washington" 

buf, err := json.Marshal(p) 
fmt.Println(string(buf), err) 

輸出將仍然是一個有效的JSON(嘗試在Go Playground):

{"Name":"John","Ticket":"New\" York - Washington"} <nil> 
+0

精彩的解決方案。謝謝。 –

+0

順便提一句,你可以使用'%q'而不是'%s',它會爲你引用字符串。 https://play.golang.org/p/RYhaMM5kpd – Kaedys

+1

@Kaedys你讓我意識到我忘記了輸入中的潛在引用。我更新了這個問題,建議使用'json'包來處理這些問題。 – icza

相關問題