MST

星途 面试题库

面试题:Go反射在复杂嵌套结构体序列化中的优化

给定一个复杂嵌套的Go结构体:type Address struct { City string `json:"city"` Country string `json:"country"` } type Company struct { Name string `json:"name"` Address Address `json:"address"` } type Employee struct { Name string `json:"name"` Age int `json:"age"` Company Company `json:"company"` } 要求使用反射实现将该结构体实例序列化为JSON格式字符串,并且要考虑性能优化,比如减少反射调用次数,你会如何实现?
45.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

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

type Address struct {
    City    string `json:"city"`
    Country string `json:"country"`
}

type Company struct {
    Name    string  `json:"name"`
    Address Address `json:"address"`
}

type Employee struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Company Company `json:"company"`
}

func serializeToJSONWithReflect(obj interface{}) ([]byte, error) {
    var result map[string]interface{}
    value := reflect.ValueOf(obj)
    if value.Kind() == reflect.Ptr {
        value = value.Elem()
    }
    if value.Kind() != reflect.Struct {
        return nil, fmt.Errorf("input must be a struct")
    }

    result = make(map[string]interface{})
    typeOf := value.Type()
    for i := 0; i < value.NumField(); i++ {
        field := value.Field(i)
        tag := typeOf.Field(i).Tag.Get("json")
        if tag == "" {
            continue
        }
        if field.Kind() == reflect.Struct {
            subResult, err := serializeToJSONWithReflect(field.Interface())
            if err != nil {
                return nil, err
            }
            var subMap map[string]interface{}
            json.Unmarshal(subResult, &subMap)
            result[tag] = subMap
        } else {
            result[tag] = field.Interface()
        }
    }
    return json.Marshal(result)
}

func main() {
    emp := Employee{
        Name: "John Doe",
        Age:  30,
        Company: Company{
            Name: "Acme Inc",
            Address: Address{
                City:    "New York",
                Country: "USA",
            },
        },
    }
    data, err := serializeToJSONWithReflect(emp)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(string(data))
}
  1. 优化思路
    • 减少反射调用次数,尽量一次遍历结构体的所有字段。
    • 对结构体嵌套结构体的情况,递归处理,并且在递归处理时也遵循减少反射调用次数的原则。
  2. 代码解释
    • serializeToJSONWithReflect 函数接受一个 interface{} 类型的参数,首先检查参数是否为指针,如果是则获取指针指向的值。
    • 检查值是否为结构体类型,如果不是则返回错误。
    • 初始化一个 map[string]interface{} 用于存储序列化后的结果。
    • 遍历结构体的所有字段,获取字段的 json tag,如果 tag 为空则跳过。
    • 如果字段是结构体类型,则递归调用 serializeToJSONWithReflect 进行序列化,并将反序列化后的 map 作为结果存储到主 map 中。
    • 如果字段不是结构体类型,则直接将字段值存储到主 map 中。
    • 最后将主 map 使用 json.Marshal 转换为JSON格式的字节切片。
  3. 性能考虑
    • 相比于每次都使用 json.Marshal 对结构体的每个部分进行序列化,这种方式减少了 json.Marshal 的调用次数,从而提升了性能。同时,一次遍历结构体字段也减少了反射的调用次数。

注意,在实际生产中,如果性能要求极高,也可以考虑使用代码生成工具(如 gogoprotobuf 类似原理)来避免反射带来的性能损耗。