如何对JSON进行反序列化?

10
我正在尝试将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

3个回答

16

这是一种不需要反射的 Go 做法。创建一个新类型 RawString 并为其创建 MarshalJSONUnmarshalJSON 方法。(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)
}

我修改了RawMessage的实现以创建上述内容。


3
这里的问题在于您正在为两个协议使用一种编码格式,可能存在模型错误。
这是有效的 Json 负载,应该作为这样处理。一个技巧是创建另一个字段来处理“字符串” JSON 和一个来处理“JSON 结构”。请参见此修改后的示例:首先取消编组 JSON,然后将其重新编组为要上传到数据库的最终字符串。一个字段用于取消编组,另一个用于与 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)
}

2
看起来问题是你代码中的 `s interface{}` 没有被寻址。对于 `Value.SetString`,该值必须是可寻址且带有 `Kind` 为字符串。可以在文档中查看 - http://golang.org/pkg/reflect/#Value.SetString 我的理解是,由于你只使用了接口 `s`,所以 `SetString` 不会改变 `a` 中的值。在Reflection的法则 中,可以找到“reflect.ValueOf 是 x 的拷贝,而不是 x 本身”(第三定律)。
为了使您的代码工作,我进行了一些类型断言,并在指针到断言结构上使用了 `reflect.ValueOf`。
要检查 `Value` 是否可设置或可寻址,可以使用Value.CanSetValue.CanAddr 工作代码: http://play.golang.org/p/DTriENkzA8 不知道这是否是正确的方法。

谢谢。但是我们如何在没有类型断言的情况下完成它呢?通常我们不会预先知道类型(s = l.(A))在UnmarshalJsonIntoStruct函数中。 - sat
@BVSat 你可以为所有可能的结构体制作一个switch/case。 - Milan Halada

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接