在`json.Marshal()`中指定结构格式。

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

Specify struct format in json.Marshal()

问题

我有以下的结构体,用于与一个API进行通信:

  1. type Object struct {
  2. Id uint64
  3. Type string
  4. Class string
  5. Properties []Property
  6. }
  7. type Property struct {
  8. Name string
  9. DataType string
  10. Value interface{}
  11. }

在发送之前,我使用json.MarshalIndent()将结构体转换为JSON,得到的结果如下:

  1. {
  2. "Id": 15,
  3. "Type": "Node",
  4. "Class": "Persona",
  5. "Properties": [
  6. {
  7. "Name": "Nombre",
  8. "DataType": "text",
  9. "Value": "Oso"
  10. },
  11. {
  12. "Name": "Edad",
  13. "DataType": "int",
  14. "Value": 45
  15. },
  16. {
  17. "Name": "Fecha de Naciemiento",
  18. "DataType": "date",
  19. "Value": "1989-09-27T05:30:08-06:00"
  20. }
  21. ]
  22. }

我想在进行编组之前格式化Value的值(因为它是interface{}类型,所以需要根据值的类型进行格式化)。

我想到的第一个解决方案是创建一个(Object) encode() string函数或类似的函数,遍历[]Property并格式化值,然后分别对每个属性进行编组,然后使用[]string重建Object,然后对对象进行编组。

是否有内置的方法可以实现这个?如果没有,是否有惯用的方法可以实现这个?

英文:

I have the following structs, which I use to communicate with an API:

  1. type Object struct {
  2. Id uint64
  3. Type string
  4. Class string
  5. Properties []Property
  6. }
  7. type Property struct {
  8. Name string
  9. DataType string
  10. Value interface{}
  11. }

And I use json.MarshalIndent() to convert my struct into a json before sending it. this gives me something like:

  1. {
  2. "Id": 15,
  3. "Type": "Node",
  4. "Class": "Persona",
  5. "Properties": [
  6. {
  7. "Name": "Nombre",
  8. "DataType": "text",
  9. "Value": "Oso"
  10. },
  11. {
  12. "Name": "Edad",
  13. "DataType": "int",
  14. "Value": 45
  15. },
  16. {
  17. "Name": "Fecha de Naciemiento",
  18. "DataType": "date",
  19. "Value": "1989-09-27T05:30:08-06:00"
  20. }
  21. ]
  22. }

I want to format the value value (because it is of type interface{} I need to format it depending on the value type) of the struct Property before marshaling it.

The first solution that occurred to me was to create a (Object) encode() string function or something, that iterates through []Property formatting the values, and marshaling each property separately, then reconstructing the Object with an []string instead of the []Property and then marshaling the object.

Is there any built-in way of doing this? If not, is there any idiomatic way of doing it?

答案1

得分: 1

JSON编码器根据值的实际类型对interface{}值进行编码。你可以通过几种方式来覆盖默认的编码方式。

第一种方式是创建一个包装器来控制值的编码方式,使用Marshaler接口。下面是一个改变整数编码方式的包装器示例:

  1. type Value struct{ Value interface{} }
  2. func (v Value) MarshalJSON() ([]byte, error) {
  3. switch v := v.Value.(type) {
  4. case int:
  5. return []byte(fmt.Sprintf("\"#%d\"", v)), nil
  6. default:
  7. return json.Marshal(v)
  8. }
  9. }

使用方法如下:

  1. prop.Value = Value{45}

playground

第二种方式是在Property类型上实现Marshaler,以覆盖整个属性(包括Value字段)的编码方式。

  1. func (p Property) MarshalJSON() ([]byte, error) {
  2. var buf bytes.Buffer
  3. buf.WriteString(`{"Name":`)
  4. d, err := json.Marshal(p.Name)
  5. if err != nil {
  6. return nil, err
  7. }
  8. buf.Write(d)
  9. buf.WriteString(`,"DataType":`)
  10. d, err = json.Marshal(p.DataType)
  11. if err != nil {
  12. return nil, err
  13. }
  14. buf.Write(d)
  15. buf.WriteString(`, "Value":`)
  16. switch v := p.Value.(type) {
  17. case int:
  18. fmt.Fprintf(&buf, "\"#%d\"", v)
  19. default:
  20. d, err := json.Marshal(v)
  21. if err != nil {
  22. return nil, err
  23. }
  24. buf.Write(d)
  25. }
  26. buf.WriteString("}")
  27. return buf.Bytes(), nil
  28. }

playground

英文:

The JSON encoder marshals interface{} values according to the actual type of the value. You can override the default encoding in a couple of ways.

The first is to create a wrapper around values to control how they are encoded using the Marshaler interface. Here's a wrapper that changes how integers are encoded:

  1. type Value struct{ Value interface{} }
  2. func (v Value) MarshalJSON() ([]byte, error) {
  3. switch v := v.Value.(type) {
  4. case int:
  5. return []byte(fmt.Sprintf("\"#%d\"", v)), nil
  6. default:
  7. return json.Marshal(v)
  8. }
  9. }

Use it like this:

  1. prop.Value = Value{45}

<kbd>playground</kbd>

A second approach is to implement the Marshaler on the Property type to override how all of the property is marshaled including the Value field.

  1. func (p Property) MarshalJSON() ([]byte, error) {
  2. var buf bytes.Buffer
  3. buf.WriteString(`{&quot;Name&quot;:`)
  4. d, err := json.Marshal(p.Name)
  5. if err != nil {
  6. return nil, err
  7. }
  8. buf.Write(d)
  9. buf.WriteString(`,&quot;DataType&quot;:`)
  10. d, err = json.Marshal(p.DataType)
  11. if err != nil {
  12. return nil, err
  13. }
  14. buf.Write(d)
  15. buf.WriteString(`, &quot;Value&quot;:`)
  16. switch v := p.Value.(type) {
  17. case int:
  18. fmt.Fprintf(&amp;buf, &quot;\&quot;#%d\&quot;&quot;, v)
  19. default:
  20. d, err := json.Marshal(v)
  21. if err != nil {
  22. return nil, err
  23. }
  24. buf.Write(d)
  25. }
  26. buf.WriteString(&quot;}&quot;)
  27. return buf.Bytes(), nil
  28. }

<kbd>playground</kbd>

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

发表评论

匿名网友

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

确定