encoding/jsonMarshalerUnmarshaler

但有一个案例比较棘手:包含转义 JSON 元素的 JSON 文档。如下所示:

{
 "id": 12345,
 "name": "Test Document",
 "payload": "{"message":"hello!"}"
}

我不建议构建像这样创建文档的应用程序,但有时候这样的情况是难以避免的,你希望像平常的 JSON 那样,一步就能解析这个文档。也许你从如下两种类型开始:

type LogEntry struct {
 ID      int    `json:"id"`
 Name    string `json:"name"`
 Payload string `json:"payload"`
}
type LogPayload struct {
 Message string `json:"message"`
}

Matt Holt 的 json-to-go 能够帮助你从 JSON 示例中生成初始结构体,不妨试一下!

LogEntry.PayloadstringLogPayloadencoding/jsonpayloadLogPayloadUnmarshalerLogPayload
func (lp *LogPayload) UnmarshalJSON(b []byte) error {
 var s string
 if err := json.Unmarshal(b, &s); err != nil {
  return err
 }
 if err := json.Unmarshal([]byte(s), lp); err != nil {
  return err
 }
 return nil
}
json.UnmarshalLogPayload
type fauxLogPayload LogPayload
fauxLogPayloadLogPayload
func (lp *LogPayload) UnmarshalJSON(b []byte) error {
 var s string
 if err := json.Unmarshal(b, &s); err != nil {
  return err
 }
 var f fauxLogPayload
 if err := json.Unmarshal([]byte(s), &f); err != nil {
  return err
 }
 *lp = LogPayload(f)
 return nil
}

现在,要解析整个文档的调用站点变得更好了,也简洁了:

func main() {
 doc := []byte(`{
  "id": 12345,
  "name": "Test Document",
  "payload": "{"message":"test"}"
 }`)
 var entry LogEntry
 if err := json.Unmarshal(doc, &entry); err != nil {
  fmt.Println("Error!", err)
 }
 fmt.Printf("%v", entry)
}

你可以在 Go Playground 找到这些代码。

encoding/decoding

Cheers!

main.go