golang struct 重载json序列化函数

480 阅读2分钟

最近工作遇到一个问题,需要解析一个前端传递过来的json,json 串大概是这样

{  
    "type":"array",  
    "items":{  
        "type":"number"  
    },  
    "properties":{  
        "name":{  
            "type":"string"  
        },  
        "id":{  
            "type":"number"  
        }  
    }  
}

外层是一个schema,属性对应items 也是一个schema,properties 对应的是一个字典,每个元素是一个schema 。

首先想到的第一个schema,items 和 properties 分别引用这个schema

type schema struct {
   Type       string
   Items      schema
   Properties map[string]schema
}

出现语法错误 Invalid recursive type 'schema' ,错误的递归类型schema。 golang 不支持strut递归创建。

不递归创建,在创建中属性保存一个指向schema对象的指针。

type schema struct {
   Type       string `json:"type"`
   Items      *schema `json:"items"`
   Properties map[string]*schema `json:"properties"`
}

语法上没啥问题。


func main(){
var res schema
str := "{\"type\":\"array\",\"items\":{\"properties\":{\"id\":{\"type\":\"number\"},\"name\":{\"type\":\"string\"}},\"type\":\"object\"}}"
if err := json.Unmarshal([]byte(str), &res); err != nil {
		panic(err)
	}
 fmt.Println(res.Items.Properties["id"].Type)
 }
 
 
type schema struct {
   Type       string             `json:"type"`
   Items      *schema            `json:"items"`
   Properties map[string]*schema `json:"properties"`
}

打印结果为 number 。

下面看个例子,重载Schema

package main

import (
   "encoding/json"
   "fmt"
)

type SchemaRef struct {
   Ref   string
   Value *Schema
}

type Schemas map[string]*SchemaRef

type Schema struct {
   Type       string     `json:"type,omitempty" yaml:"type,omitempty"`
   Items      *SchemaRef `json:"items,omitempty" yaml:"items,omitempty"`
   Properties Schemas    `json:"properties,omitempty" yaml:"properties,omitempty"`
}

func (schemaRef *SchemaRef) MarshalJSON() (res []byte, err error) {
   schema := *schemaRef.Value
   res, err = json.Marshal(schema)
   if err != nil {
      return
   }
   return
}

func (schemaRef *SchemaRef) UnmarshalJSON(data []byte) error {
   var schema Schema
   err := json.Unmarshal(data, &schema)
   if err != nil {
      return err
   }
   schemaRef.Ref = ""
   schemaRef.Value = &schema
   return nil
}

func main() {
   var res Schema
   str := "{"type":"array","items":{"properties":{"id":{"type":"number"},"name":{"type":"string"}},"type":"object"}}"
   if err := json.Unmarshal([]byte(str), &res); err != nil {
      panic(err)
   }
   fmt.Println(res.Items.Value.Properties["id"].Value.Type)

   jsonStr, _ := json.Marshal(&res)
   fmt.Println(string(jsonStr))
}
  1. Items 属性指向schemaRef,Schemas 为 map[string]*SchemaRef ,通过重载来接受json转换。
  2. 定义了 UnmarshalJSON 方法,此方法解析json之后,然后对schemaRef 本身赋值,改变了json原本的结构。
  3. 定义了 MarshalJSON 此方法,把 schemaRef.Value 拿出来序列化成字符串,然后返回。
  4. 本质上还是沿用json 包的基本方法,然后对此做了调整。