如何解析JSON?

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

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.

http://play.golang.org/p/YzrhjuXxGN

答案1

得分: 15

这是使用Go语言的方法,不需要使用反射。首先创建一个新类型RawString,然后为它创建MarshalJSONUnmarshalJSON方法。下面是代码示例:

// 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.CanSetValue.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

huangapple
  • 本文由 发表于 2014年5月22日 13:13:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/23798283.html
匿名

发表评论

匿名网友

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

确定