英文:
How do I Unmarshal JSON?
问题
我正在尝试将JSON解组成一个结构体。然而,该结构体有一个带有标签的字段。使用反射,我试图查看标签中是否包含字符串"json"。如果包含,则应将要解组的JSON简单地解组为字符串并赋值给该字段。
示例:
const data = `{"I":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S string `sql:"type:json"`
}
问题很简单 - 将JSON中的"S"字段解组为字符串并赋值给结构体A。
这是我目前的进展。但我在这里卡住了。
http://play.golang.org/p/YzrhjuXxGN
英文:
I am trying to unmarshal JSON into a struct. However, the struct has a field with a tag. Using reflection, and I try to see if the tag has the string "json" in it. If it does, then the json to unmarshal should simply be unmarshaled into the field as a string.
Example:
const data = `{"I":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S string `sql:"type:json"`
}
Problem is simple - unmarshal "S" in the json as a string into the struct A.
This is how far I have come. But I am stuck here.
答案1
得分: 15
这是使用Go语言的方法,不需要使用反射。首先创建一个新类型RawString
,然后为它创建MarshalJSON
和UnmarshalJSON
方法。下面是代码示例:
// RawString是一个原始编码的JSON对象。
// 它实现了Marshaler和Unmarshaler接口,可以用于延迟JSON解码或预先计算JSON编码。
type RawString string
// MarshalJSON将*m转换为m的JSON编码。
func (m *RawString) MarshalJSON() ([]byte, error) {
return []byte(*m), nil
}
// UnmarshalJSON将*m设置为data的副本。
func (m *RawString) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
*m += RawString(data)
return nil
}
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S RawString `sql:"type:json"`
}
func main() {
a := A{}
err := json.Unmarshal([]byte(data), &a)
if err != nil {
log.Fatal("Unmarshal failed", err)
}
fmt.Println("Done", a)
}
我修改了RawMessage的实现来创建上述代码。
英文:
This is the go way of doing it - no reflection requred. Create a new type RawString
and create MarshalJSON
and UnmarshalJSON
methods for it. (playground)
// RawString is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawString string
// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawString) MarshalJSON() ([]byte, error) {
return []byte(*m), nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawString) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("RawString: UnmarshalJSON on nil pointer")
}
*m += RawString(data)
return nil
}
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
S RawString `sql:"type:json"`
}
func main() {
a := A{}
err := json.Unmarshal([]byte(data), &a)
if err != nil {
log.Fatal("Unmarshal failed", err)
}
fmt.Println("Done", a)
}
I modified the implementation of RawMessage to create the above.
答案2
得分: 3
问题在于你正在为两个协议使用同一种编码格式,可能在你的模型中有一些问题。
这是一个有效的 JSON 负载,并且应该按照这样的方式处理。你可以使用一个技巧,创建另一个字段来处理“字符串” JSON,另一个字段来处理“JSON 结构”。看看这个修改后的示例:我首先对 JSON 进行解组,然后再次对其进行编组,以创建要上传到数据库的最终字符串。一个字段用于解组,另一个字段用于与数据库通信。
package main
import (
"fmt"
"encoding/json"
)
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
Sjson struct {
Phone struct {
Sales string `json:"sales"`
} `json:"phone"`
} `json:"S", sql:"-"`
S string `sql:"type:json",json:"-"`
}
func main() {
msg := A{}
_ = json.Unmarshal([]byte(data), &msg)
data, _ := json.Marshal(msg.Sjson)
msg.S = string(data)
fmt.Println("Done", msg)
}
希望对你有所帮助!
英文:
The problem here is that you are using one encoding format for two protocols and probably there is something wrong in your model.
That is a valid Json payload and should be handled as such. One trick that you can use is to create
another field to handle the "string" json and one to handle the "json struct".
See this modified example: I first unmarshal json and then marshal back to json to create the final string to upload to the database. One field is used for unmarshaling and the other to communicate with the DB.
package main
import(
"fmt"
"encoding/json"
)
const data = `{"i":3, "S":{"phone": {"sales": "2223334444"}}}`
type A struct {
I int64
Sjson struct {
Phone struct {
Sales string `json:"sales"`
} `json:"phone"`
} `json:"S", sql:"-"`
S string `sql:"type:json",json:"-"`
}
func main() {
msg := A{}
_ = json.Unmarshal([]byte(data), &msg)
data, _ := json.Marshal(msg.Sjson)
msg.S = string(data)
fmt.Println("Done", msg)
}
答案3
得分: 2
看起来问题在于你的代码中的s interface{}
是不可寻址的。对于Value.SetString
,值必须是可寻址的,并且类型必须是Kind
为String。你可以查看相关文档了解更多信息- http://golang.org/pkg/reflect/#Value.SetString
根据我的理解,SetString
不会改变a
中的值,因为你只是在处理接口s
。在《反射法则》(http://blog.golang.org/laws-of-reflection)中,你可以找到“reflect.ValueOf是x的副本,而不是x本身”(第三法则)。
为了使你的代码工作,我进行了一些类型断言,并在断言的结构体指针上使用了reflect.ValueOf
。
要检查Value
是否可设置或可寻址,你可以使用Value.CanSet
和Value.CanAddr
。
工作的代码示例:http://play.golang.org/p/DTriENkzA8
不确定这是否是正确的做法。
英文:
Looks like the problem is that s interface{}
in your code was not addressable. For Value.SetString
the Value has to be addressable and with Kind
String. you can check the documentation for it - http://golang.org/pkg/reflect/#Value.SetString
How i understand it SetString
would not change the value in a
, since you are only working with interface s
. in Laws of Reflection you can find "reflect.ValueOf is a copy of x, not x itself"(3rd Law).
To make your code work I made some type assertions, and I used reflect.ValueOf
on a pointer to asserted struct.
To check if Value
is settable or addressable you can use Value.CanSet
ad Value.CanAddr
working code: http://play.golang.org/p/DTriENkzA8
No idea whether its correct way to do this
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论