英文:
How do I json.Unmarshal based on a type
问题
我正在为Flowdock API编写一个Go客户端。他们的Message API有许多属性,其中两个是Event
和Content
。
当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
字符串的一个片段。
所以我的问题是:
- 我应该只去掉引号吗?
- 如果是,Go中最简单的去除引号的方法是什么?也许是这个:http://play.golang.org/p/kn8XKOqF0z(第6行,第19行)?
- 这种处理同一属性可能具有不同类型的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:
- Should I just strip off the quotes?
- If so what is the simplest way in Go to strip? maybe this: http://play.golang.org/p/kn8XKOqF0z lines(6, 19)?
- 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论