Json数据适配器

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

Json data adapter

问题

我想将JSON中的

{ name: "http://example.com", ...}

映射为

{ url: "http://example.com", ...}

我想要进行这种映射,并且能够将其作为Url重新编组为json。由于结构体相当大,所以是否可能只使用一个结构体而不是创建多个结构体类型来实现这一点?

这是我目前想到的解决方案:http://play.golang.org/p/wBPbSjkTYF

在看到答案并且意识到我未能准确描述我的用例之后,我更新了实际要进行的映射:

{ "data": { "name": "http://example.com", "key": "value" } }

到:

{ "url": "http://example.com", { "data": "key": "value" } }
英文:

I want to take JSON in

{ name: "http://example.com", ...}

and map it to

{ url: "http://example.com", ...}

I want to do this mapping and be able to Marshal back to json as Url. The struct is rather large, so is it possible to do this with one struct instead creating multiple struct types?

This is the solution I have come to so far: http://play.golang.org/p/wBPbSjkTYF

After seeing answers and how I failed to accurately describe my use case. Updating to the actual mapping I'm trying to do:

{ "data": { "name": "http://example.com", "key": "value" } }

To:

{ "url": "http://example.com", { "data": "key": "value" } }

答案1

得分: 1

json包没有任何标签可以让你在Marshal和Unmarshal时使用不同的字段键名。

但是,仍然可以以不同的方式将结构体编组,但为了做到这一点,你需要实现自己的编组器(MarshalJSON函数)。下面是一个工作示例:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

type Data struct {
	Url string `json:"name"`
}

// marshalObject接受一个键值对的切片
// (应该是正确转义的不带引号的JSON字符串)
// 和另一个interface{}值的切片,并将它们编组为JSON对象字符串
func marshalObject(keys []string, values []interface{}) ([]byte, error) {

	if len(keys) != len(values) {
		panic("Different length of keys and values slices")
	}

	if len(keys) == 0 {
		return []byte(`{}`), nil
	}

	var b bytes.Buffer
	b.Write([]byte(`{"`))

	for i, key := range keys {
		if i != 0 {
			b.Write([]byte(`,"`))
		}
		b.WriteString(key)
		b.Write([]byte(`":`))
		j, err := json.Marshal(values[i])
		if err != nil {
			return nil, err
		}
		b.Write(j)
	}

	b.Write([]byte(`}`))

	return b.Bytes(), nil
}

func (d *Data) MarshalJSON() ([]byte, error) {
	// 在这里你可以添加一系列的键和值。
	// 目前只有`url`映射到d.Url
	return marshalObject(
		[]string{
			"url",
		},
		[]interface{}{
			d.Url,
		},
	)
}

func main() {
	i := []byte(`{"name":"http://example.com"}`)
	fmt.Printf("Json Input: %+s\n", i)

	var d *Data
	err := json.Unmarshal(i, &d)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Data: %#v\n", d)

	o, err := json.Marshal(d)
	if err != nil {
		panic(err)
	}
	fmt.Printf("Json Output: %+s\n", o)

}

结果:

Json Input: {"name":"http://example.com"}
Data: &main.Data{Url:"http://example.com"}
Json Output: {"url":"http://example.com"}

Playground:

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

英文:

The json package doesn't have any tags that allows you to use one key name of a field on Marshal and a different on Unmarshal.

It is still possible to Marshal a struct to differently, but in order to do that you will have to implement your own Marshaller (MarshalJSON function). A working example:

package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Data struct {
Url string `json:"name"`
}
// marshalObject takes a slice of key values
// (should be correctly escaped json strings without "")
// and another slice of interface{} values and marshals
// them into a JSON object string
func marshalObject(keys []string, values []interface{}) ([]byte, error) {
if len(keys) != len(values) {
panic("Different length of keys and values slices")
}
if len(keys) == 0 {
return []byte(`{}`), nil
}
var b bytes.Buffer
b.Write([]byte(`{"`))
for i, key := range keys {
if i != 0 {
b.Write([]byte(`,"`))
}
b.WriteString(key)
b.Write([]byte(`":`))
j, err := json.Marshal(values[i])
if err != nil {
return nil, err
}
b.Write(j)
}
b.Write([]byte(`}`))
return b.Bytes(), nil
}
func (d *Data) MarshalJSON() ([]byte, error) {
// Here you can add a list of keys and values.
// Currently it is only `url` mapped to d.Url
return marshalObject(
[]string{
`url`,
},
[]interface{}{
d.Url,
},
)
}
func main() {
i := []byte(`{"name":"http://example.com"}`)
fmt.Printf("Json Input: %+s\n", i)
var d *Data
err := json.Unmarshal(i, &d)
if err != nil {
panic(err)
}
fmt.Printf("Data: %#v\n", d)
o, err := json.Marshal(d)
if err != nil {
panic(err)
}
fmt.Printf("Json Output: %+s\n", o)
}

Result:

Json Input: {"name":"http://example.com"}
Data: &main.Data{Url:"http://example.com"}
Json Output: {"url":"http://example.com"}

Playground:

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

答案2

得分: 0

如果你将一个JSON对象解组成一个map[string]interface{}变量,你将得到对象中的所有内容,而不仅仅是一些字段。要进行所需的修改应该相当简单,像这样:

func nameToUrl(data []byte) ([]byte, error) {
    var obj map[string]interface{}
    if err := json.Unmarshal(data, &obj); err != nil {
        return nil, err
    }
    if name, ok := obj["name"]; ok {
        delete(obj, "name")
        obj["url"] = name
    }
    return json.Marshal(obj)
}

你可以在这里进行实验:http://play.golang.org/p/0jz_HAGg3E

英文:

If you unmarshal a JSON object into an map[string]interface{} variable, you will be given everything in the object rather than just a few fields. It should be fairly straight forward to make the required modifications, like so:

func nameToUrl(data []byte) ([]byte, error) {
var obj map[string]interface{}
if err := json.Unmarshal(data, &obj); err != nil {
return nil, err
}
if name, ok := obj["name"]; ok {
delete(obj, "name")
obj["url"] = name
}
return json.Marshal(obj)
}

You can experiment with this here: http://play.golang.org/p/0jz_HAGg3E

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

发表评论

匿名网友

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

确定