根据类型如何进行json.Unmarshal操作?

huangapple go评论91阅读模式
英文:

How do I json.Unmarshal based on a type

问题

我正在为Flowdock API编写一个Go客户端。他们的Message API有许多属性,其中两个是EventContent

Event = message时,Content是一个字符串。当Event = comment时,Content是一个JSON对象。

我希望在需要时延迟解析Content。为此,我在我的结构体中映射了RawContent,并在Message上定义了一个Content()方法来返回正确的对象。

以下是示例代码:

package main

import (
	"fmt"
	"encoding/json"
)

// Message表示Flowdock聊天消息。
type Message struct {
	Event      *string          `json:"event,omitempty"`
	RawContent *json.RawMessage `json:"content,omitempty"`
}

func (m *Message) Content() (content interface{}) {
	// 当m.Event是message时,RawContent是一个字符串
	// 实际代码处理其他类型的解组(在此示例中已删除)
	return string(*m.RawContent)
}

func main() {
	var message = &Message{}
	rawJSON := `{"event": "message", "content": "Howdy-Doo @Jackie #awesome"}`
	if err := json.Unmarshal([]byte(rawJSON), &message); err != nil {
		panic(err.Error())
	}

	event := "message"
	rawMessage := json.RawMessage("Howdy-Doo @Jackie #awesome")
	want := &Message{
		Event:      &event,
		RawContent: &rawMessage,
	}

	fmt.Println(message.Content(), want.Content())
}

运行结果是:http://play.golang.org/p/eds_AA6Aay

"Howdy-Doo @Jackie #awesome" Howdy-Doo @Jackie #awesome

注意:message.Content()want.Content()是不同的!

起初,我没有预料到引号会包含在message中,但考虑到JSON的解析方式,这是有道理的。它只是整个rawJSON字符串的一个片段。

所以我的问题是:

  1. 我应该只去掉引号吗?
  2. 如果是,Go中最简单的去除引号的方法是什么?也许是这个:http://play.golang.org/p/kn8XKOqF0z(第6行,第19行)?
  3. 这种处理同一属性可能具有不同类型的JSON的方法是否正确?

这里有一个更完整的示例,展示了我如何处理JSON的RawContent:http://play.golang.org/p/vrBJ5RYcql

英文:

I am writing a go client for the Flowdock API. Their API for Message has a number of attributes two of which are Event and Content

When Event = message then Content is a string. When Event = comment Content is a JSON object.

I want to defer unmarhsalling Content until it is needed. To do this I map RawContent in my struct and define a Content() method on Message to return the correct object.

Here is the code to illustrate:

package main

import (
	"fmt"
	"encoding/json"
)

// Message represents a Flowdock chat message.
type Message struct {
	Event            *string          `json:"event,omitempty"`
	RawContent       *json.RawMessage `json:"content,omitempty"`
}


func (m *Message) Content() (content interface{}) {
    // when m.Event is a message the RawContent is a string
	// real code handles unmarshaling other types (removed for this example)
	return string(*m.RawContent)
}

func main() {
	var message = &Message{}
	rawJSON := `{"event": "message", "content": "Howdy-Doo @Jackie #awesome"}`
	if err := json.Unmarshal([]byte(rawJSON), &message); err != nil {
		panic(err.Error())
	}
	
	event := "message"
	rawMessage := json.RawMessage("Howdy-Doo @Jackie #awesome")
	want := &Message{
		Event: &event,
		RawContent: &rawMessage,
	}
	
	fmt.Println(message.Content(), want.Content())
}

The result of running this is: http://play.golang.org/p/eds_AA6Aay

"Howdy-Doo @Jackie #awesome" Howdy-Doo @Jackie #awesome

Note: message.Content() and want.Content() are different!

At first I did not expect the quotes to be included in message but it makes sense because of how the JSON is parsed. It is just a slice of the whole rawJSON string.

So my questions are:

  1. Should I just strip off the quotes?
  2. If so what is the simplest way in Go to strip? maybe this: http://play.golang.org/p/kn8XKOqF0z lines(6, 19)?
  3. Is this even the right approach to handling JSON that can have different types for the same attribute?

Here is a more complete example showing how I handle a JSON RawContent: http://play.golang.org/p/vrBJ5RYcql

答案1

得分: 4

问题1:

不,当内容只包含一个字符串时,你应该使用json.Unmarshal。除了引号之外,JSON字符串还可以包含反斜杠转义的控制字符。

问题2:

根据问题1的答案,在“message”情况下执行以下操作:

var s string		
if err := json.Unmarshal([]byte(*m.RawContent), &s); err != nil {
    panic(err)
}
return s

问题3:

将事件类型字符串解析为结构体,并使用RawMessage存储JSON的其余部分,直到评估类型并知道内容具有什么样的结构,这是一个很好的方法。

你可能还想考虑为简单的字符串内容定义一个特定的类型,例如:

type MessageContent string

这样,你可以在该类型上实现方法,允许Content方法返回除空接口interface{}之外的其他接口。

注意:

请注意,如果按照我的建议,你还将对消息字符串进行json.Unmarshal,那么当尝试对非引号引起来的want.RawContent字符串进行解析时,你的 Playground 示例将会引发 panic,因为它不是有效的 JSON。

英文:

Question 1:

No, you should json.Unmarshal the content also when it only contains a string. Apart from the quotes, JSON strings may also contain backslash-escaped control characters.

Question 2:

Because of the answer in Question 1, do the following for the "message" case:

var s string		
if err := json.Unmarshal([]byte(*m.RawContent), &s); err != nil {
	panic(err)
}
return s

Question 3:

It is a good approach to Unmarshal the event type string and use RawMessage to store the rest of the JSON until you've evaluated the type and know what kind of structure the content has.

You might want to consider also having a specific type also for just simple string contents, eg.:

type MessageContent string

This way you are free to implement methods on the type, allowing the Content method to return some other interface than just the empty interface{}.

Note:

Beware that, if you also json.Unmarshal the message string as I suggested, your Playground example will panic when trying to Unmarshal your non-quoted want.RawContent string, because it is not valid JSON.

huangapple
  • 本文由 发表于 2014年1月6日 09:05:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/20941375.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定