Go语言JSON序列化实战:从基础到进阶的优雅实现

80 阅读5分钟

Go语言JSON序列化实战:从基础到进阶的优雅实现

在Go语言开发中,JSON序列化是一个绕不开的核心技能。无论是API开发、数据传输,还是日志处理,我们都需要将Go结构体高效、准确地转换为JSON格式。今天,我们将深入剖析一组实用的JSON序列化函数,从基础字节切片转换到格式化输出,带你掌握Go中JSON处理的精髓。无论你是初学者还是进阶开发者,这篇文章都将为你提供灵感和实践参考!

本文介绍了一组Go语言中实现JSON序列化的实用函数,包括ToJSON、ToJSONString和ToPrettyJSON,并通过详细的测试用例展示了它们在不同场景下的表现。代码不仅覆盖了基本的数据转换,还处理了nil值、不可序列化输入及循环引用等边缘情况。无论你是需要快速生成JSON字节流,还是追求格式化输出的优雅呈现,这些实现都能满足需求。文章还附带测试代码和参考资源,助你快速上手并优化项目。

实操

package experiments

import (
 "encoding/json"
 "fmt"
)

// ToJSON 将一个 Go 语言值转换为 JSON 格式的字节切片。
// 该函数接受一个 interface{} 类型的参数 v,这意味着它可以接受任何类型的值。
// 使用 json.Marshal 函数将 v 转换为 JSON 格式。如果转换过程中发生错误,这个错误将被忽略。
// 返回值是转换后的 JSON 格式的字节切片。如果 v 无法被转换为 JSON 格式,则返回空的字节切片。
func ToJSON(v interface{}) []byte {
 b, _ := json.Marshal(v)
 return b
}

// ToJSONString 将给定的值转换为JSON格式的字符串。
// 如果输入值为nil,则根据需求决定返回"null"或空字符串。
// 参数:
//
// v - 要转换为JSON字符串的值。
//
// 返回值:
//
// JSON格式的字符串表示,如果发生错误则返回空字符串。
func ToJSONString(v interface{}) string {
 // 检查输入值是否为nil,如果是,则根据需求返回"null"或空字符串。
 if v == nil {
  // 根据需求决定是否返回 "null" 或空字符串
  return "null"
 }

 // 尝试将输入值转换为JSON格式的字节切片。
 b, err := json.Marshal(v)
 // 如果转换过程中发生错误,则记录错误信息并返回空字符串。
 if err != nil {
  // 记录错误日志(如果需要)
  fmt.Println("Error during JSON marshaling:", err)
  // 返回空字符串或自定义错误信息
  return ""
 }
 // 如果转换成功,则将字节切片转换为字符串并返回。
 return string(b)
}

// ToPrettyJSON 将给定的接口类型数据转换为格式化的 JSON 字符串。
// 如果输入数据为 nil,返回 "null" 的 JSON 表示。
// 如果输入数据无法序列化为 JSON,返回包含错误信息的 JSON 字符串。
func ToPrettyJSON(v interface{}) string {
 // 如果 v 为 nil,直接返回 "null" 的 JSON 表示
 if v == nil {
  return "null"
 }

 // 使用 json.MarshalIndent 将数据格式化为带缩进的 JSON 字符串
 b, err := json.MarshalIndent(v, """  ")
 if err != nil {
  // 如果序列化失败,返回一个明确的错误提示
  return fmt.Sprintf(`{"error": "%v"}`, err)
 }

 // 返回格式化后的 JSON 字符串
 return string(b)
}

这段代码为我们提供了三种 JSON 序列化方式:ToJSON 快速生成字节切片,适合高效传输;ToJSONString 输出字符串,兼顾 nil 和错误处理,便于日志或调试;ToPrettyJSON 则带来格式化输出,调试时一目了然。无论是性能还是可读性,这组函数都为 Go 开发者提供了实用工具,接下来我们通过测试看看它们的实际表现吧!

测试

package tests

import (
 "github.com/qiaopengjun5162/GopherNest/experiments"
 "strings"
 "testing"
)

// TestToJSON_ValidInput_ReturnsJSON tests that ToJSON correctly converts a valid input to JSON.
func TestToJSON_ValidInput_ReturnsJSON(t *testing.T) {
 type Person struct {
  Name string
  Age  int
 }

 person := Person{Name: "John Doe", Age: 30}
 expectedJSON := `{"Name":"John Doe","Age":30}`

 actualJSON := experiments.ToJSON(person)

 if string(actualJSON) != expectedJSON {
  t.Errorf("ToJSON(%v) = %s; want %s", person, actualJSON, expectedJSON)
 }
}

// TestToJSON_InvalidInput_ReturnsEmpty tests that ToJSON returns an empty byte slice for invalid input.
func TestToJSON_InvalidInput_ReturnsEmpty(t *testing.T) {
 type Invalid struct {
  Foo func() // Functions are not JSON serializable
 }

 invalid := Invalid{Foo: func() {}}

 actualJSON := experiments.ToJSON(invalid)

 if len(actualJSON) != 0 {
  t.Errorf("ToJSON(%v) = %s; want empty byte slice", invalid, actualJSON)
 }
}

// TestToJSONString_NilInput_ReturnsNull tests the behavior of ToJSONString when the input is nil.
func TestToJSONString_NilInput_ReturnsNull(t *testing.T) {
 result := experiments.ToJSONString(nil)
 if result != "null" {
  t.Errorf("Expected 'null' for nil input, got '%s'", result)
 }
}

// TestToJSONString_ValidInput_ReturnsJSONString tests the behavior of ToJSONString with a valid input.
func TestToJSONString_ValidInput_ReturnsJSONString(t *testing.T) {
 input := map[string]string{"key""value"}
 expected := `{"key":"value"}`
 result := experiments.ToJSONString(input)
 if result != expected {
  t.Errorf("Expected '%s', got '%s'", expected, result)
 }
}

// TestToJSONString_UnserializableInput_ReturnsEmptyString tests the behavior of ToJSONString with an unserializable input.
func TestToJSONString_UnserializableInput_ReturnsEmptyString(t *testing.T) {
 type Unserializable struct {
  Chan chan int
 }
 input := Unserializable{Chan: make(chan int)}
 result := experiments.ToJSONString(input)
 if result != "" {
  t.Errorf("Expected empty string for unserializable input, got '%s'", result)
 }
}

func TestToPrettyJSON_NilInput_ReturnsNull(t *testing.T) {
 result := experiments.ToPrettyJSON(nil)
 if result != "null" {
  t.Errorf("Expected 'null' for nil input, got %s", result)
 }
}

func TestToPrettyJSON_ValidStruct_ReturnsFormattedJSON(t *testing.T) {
 type Person struct {
  Name string `json:"name"`
  Age  int    `json:"age"`
 }
 person := Person{Name: "John Doe", Age: 30}
 expected := `{
  "name": "John Doe",
  "age": 30
}`
 result := experiments.ToPrettyJSON(person)
 if result != expected {
  t.Errorf("Expected %s, got %s", expected, result)
 }
}

func TestToPrettyJSON_CircularReference_ReturnsErrorJSON(t *testing.T) {
 type Node struct {
  Next *Node `json:"next"`
 }
 node := &Node{}
 node.Next = node // 创建循环引用

 result := experiments.ToPrettyJSON(node)
 if !strings.Contains(result, "error") {
  t.Errorf("Expected error JSON for circular reference, got %s", result)
 }
}

总结

通过本文,我们探索了Go语言中JSON序列化的三种实用实现:ToJSON提供了高效的字节切片输出,ToJSONString兼顾了字符串转换的灵活性,而ToPrettyJSON则以格式化输出提升了可读性。这些函数在实际项目中各有千秋,能够应对从简单数据处理到复杂调试场景的多种需求。结合精心设计的测试用例,我们不仅验证了功能的正确性,还展示了如何处理异常情况。希望这些代码和思路能为你的Go开发之旅增添一份助力!想深入学习?文末参考资源不容错过!

参考