最近,我的团队的Lãder发现了GO上与Kude0有关的错误。他向我们展示了发生了什么事,然后我们讨论了发生这种情况的风险。这让我很感兴趣,并决定更多地调查当操纵JSON时,在不采取适当的预防措施时会发生什么。
因此,让我们开始对GO上的kude1和kude0的函数进行一些讨论,然后显示如果我们不采取适当的预防措施,可能会发生错误。很多?
JSON在GO中的本地操纵:元帅和Unmarshal
在GO程序语言中以JSON格式操纵数据时,**Marshal()**
和**Unmarshal()**
函数是JSON中允许结构(或结构(如果您喜欢的话))之间转换的功能强大的工具分别。但是,重要的是要了解与这些操作相关的可能危险,并采取一些实践以避免可节省您数小时的净化的微妙错误。
错误1:忘记出口字段
从常见错误开始,这很容易解决,但是如果您不小心,可以找到一些复杂的问题。请记住,在GO中,变量,功能,全部或字段的可见性取决于其名称的第一个字母,即mini -smell或Mayssscula,表明它分别是私人或公共的。因此,在使用**Marshal()**
和**Unmarshal()**
工作时,请记住,只有在结构中的出口字段,即公开(从May -Scula字母开始)。未平面字段将不包含在序列化(对象/结构向JSON的转换)中,或在Deerialization期间以零值完成(JSON的转换为对象/结构)。
让我们看一下此错误的简单示例。我们的用户结构有两个字段:用户的名称和密码,一个是公共的,另一个是私有的。因此,在尝试序列化时,JSON将不包含密码,并且应得的,我们将看到密码没有填充:
package main
import (
"encoding/json"
"fmt"
)
type Usuario struct {
NombreDeUsuario string `json:"nombre_de_usuario"`
Contraseña string `json:"contraseña"` // campo no exportado
}
func main() {
// Caso de serialización
jose := Usuario{NombreDeUsuario: "jose", Contraseña: "contraseña-de-jose"}
stringJson, errMarshal := json.Marshal(jose) // El campo 'Contraseña' no se incluirá porque es privado
if errMarshal != nil {
fmt.Println(errMarshal.Error())
panic(errMarshal)
}
fmt.Println("json de jose: " + string(stringJson))
// Caso de deserialización
var maria Usuario
jsonMaria := []byte("{\"nombre_de_usuario\":\"maria\", \"contraseña\":\"contraseña-de-maria\"}")
errUnmarshal := json.Unmarshal(jsonMaria, &maria)
if errUnmarshal != nil {
fmt.Println(errUnmarshal.Error())
panic(errUnmarshal)
}
fmt.Println(fmt.Sprintf("estructura de maria: %+v", maria))
}
执行kude7命令时,我们可以在控制台上看到以下出口:
json de jose: {"nombre_de_usuario":"jose"}
estructura de maria: {NombreDeUsuario:maria Contraseña:}
避免浪费时间试图发现为什么没有保留特定字段,请始终确保仅导出必须序列化的字段。
package main
import (
"encoding/json"
"fmt"
)
type Usuario struct {
NombreDeUsuario string `json:"nombre_de_usuario"`
Contraseña string `json:"contraseña"` // campo no exportado
}
func main() {
// Caso de serialización
jose := Usuario{NombreDeUsuario: "jose", Contraseña: "contraseña-de-jose"}
stringJson, errMarshal := json.Marshal(jose) // El campo 'Contraseña' no se incluirá porque es privado
if errMarshal != nil {
fmt.Println(errMarshal.Error())
panic(errMarshal)
}
fmt.Println("json de jose: " + string(stringJson))
// Caso de deserialización
var maria Usuario
jsonMaria := []byte("{\"nombre_de_usuario\":\"maria\", \"contraseña\":\"contraseña-de-maria\"}")
errUnmarshal := json.Unmarshal(jsonMaria, &maria)
if errUnmarshal != nil {
fmt.Println(errUnmarshal.Error())
panic(errUnmarshal)
}
fmt.Println(fmt.Sprintf("estructura de maria: %+v", maria))
}
执行解决方案时,我们将获得以下输出:
json de jose: {"nombre_de_usuario":"jose"}
estructura de maria: {NombreDeUsuario:maria Contraseña:}
错误2:请勿验证元帅()的输入数据
第二个错误是我最近看到的,并记得那是一段时间以前,在媒介中的巴哈尔·沙阿(Bahar Shah)文章中: 。”标题具有描述性,并且具有很大的意义:在Kude0中,返回的错误仅指json形成不佳,无法验证字段。因此,如果您收到任何JSON,即使它不包含其结构的任何字段,Deerialization被视为条目,即没有错误,但其对象/结构将不包含任何值。
因此,在使用**Unmarshal()**
函数之前,重要的是验证输入数据以避免JSON的错误。为了进行验证,关于GO有一些实践,例如从头开始创建自己的验证功能或使用验证器软件包来简化声明和验证。让我们看一个使用验证器的示例,因为它是可以扩展的解决方案,以获得完全完整的验证。
我们的代码收到一个带有有关狗的信息的JSON,真相吗?在此验证中,我们的对象具有3个字段:名称,年龄和eAmigable,每个字段都有自己的验证。例如,名称是强制性的(必需),并且必须至少具有3个字符和至少12个字符(min = 3,max = 12),而年龄是强制性的,必须是数字(数字),最后,最后,最后,这是强制性的。现在,让我们想象我们会错误地收到狗数据,但是缺少一个字段。通过此验证,我们将避免将不完整的数据保存在数据库中。让我们以错误的方式查看下面的代码:
package main
import (
"encoding/json"
"errors"
"fmt"
"github.com/go-playground/validator/v10"
)
type Perro struct {
Nombre string `json:"nombre" validate:"required,min=3,max=12"`
Edad int `json:"edad" validate:"required,numeric"`
EsAmigable bool `json:"es_amigable" validate:"required"`
}
type ErroresDeValidacion struct {
Campo string
Tag string
Valor string
}
var Validator = validator.New()
func validaPerro(perro Perro) []ErroresDeValidacion {
errores := make([]ErroresDeValidacion, 0)
err := Validator.Struct(perro)
if err != nil {
for _, errorValidator := range err.(validator.ValidationErrors) {
error := ErroresDeValidacion{
Campo: errorValidator.Field(),
Tag: errorValidator.Tag(),
Valor: errorValidator.Param(),
}
errores = append(errores, error)
}
return errores
}
return nil
}
func main() {
jsonPerro := []byte("{\"nombre\":\"bilu\", \"edad\":5}")
var perro Perro
errUnmarshal := json.Unmarshal(jsonPerro, &perro)
if errUnmarshal != nil {
fmt.Println(errUnmarshal.Error())
panic(errUnmarshal)
}
erroresValidacion := validaPerro(perro)
if len(erroresValidacion) > 0 {
fmt.Println(fmt.Sprintf("errores: %+v", erroresValidacion))
panic(errors.New("error al validar el JSON del perro"))
}
fmt.Println(fmt.Sprintf("perro: %+v", perro))
}
使用go run error_no_validado.go
运行代码时,我们将获得以下输出:
errores: [{Campo:EsAmigable Tag:required Valor:}]
panic: error al validar el JSON del perro
goroutine 1 [running]:
main.main()
camino/a/tu/codigo/erro_nao_validando_campos.go:54
exit status 2
现在,如果我们在JSON中使用校正执行相同的代码:
jsonPerro := []byte("{\"nombre\":\"bilu\", \"edad\":5, \"es_amigable\":true}")
perro: {Nombre:bilu Edad:5 EsAmigable:true}
错误3:使用元帅()时,无限的散装在圆形参考中!
现在,如果您必须处理代码中的循环引用(例如非常具体的内容,例如操纵二进制ârobol),则可能发生错误程序锁定或过度消费资源。
您可能试图想象发生这种情况的情况,好吧,我将尽可能简化它:想象一下您正在创建一个社交网络的情况,您需要返回玛丽亚数据,玛丽亚的数据在他的朋友列表中都有。因此,您从数据库中获得了María和Juan的数据,现在您将完成他们的友谊关系,然后将代表Marãa的对象转换为一系列字节(GOS的字节矩阵),以将其返回API,因为我将将这些数据转换为终点的响应链。如果您将对象转换为将其转换为一系列字节以将其返回API,请不要担心,在另一篇文章中,请说明更多。
好吧,这里有一个带有错误的代码的示例:
package main
import (
"encoding/json"
"fmt"
)
type Persona struct {
Nombre string `json:"nombre"`
Amigos []*Persona `json:"amigos"`
}
func main() {
maria := &Persona{Nombre: "Maria"}
juan := &Persona{Nombre: "Juan"}
maria.Amigos = []*Persona{juan}
juan.Amigos = []*Persona{maria}
data, err := json.Marshal(maria) // Puede entrar en un ciclo infinito aquí
if err != nil {
fmt.Println(err.Error())
panic(err)
}
fmt.Println(string(data))
}
使用go run rede_social_loop_infinito_marshal.go
执行上一个代码时,您会看到GO准备检测到此周期并返回错误。出口将如下:
json: unsupported value: encountered a cycle via *main.Persona
panic: json: unsupported value: encountered a cycle via *main.Persona
goroutine 1 [running]:
main.main()
camino/a/tu/codigo/rede_social_loop_infinito_marshal.go:22
exit status 2
现在,要解决我们产生的问题,我们可以通过两种方式来做到这一点。首先是要求在JSON中忽略该领域,我们在使用JSON的字段声明中忽略了该领域:“ - ”而不是JSON:“ Friends”。但这不会向我们发送朋友列表,我们想发送它?然后,让我们使用中间格式以序列化来解决。通常,这种格式将更简化。在这里,您有代码:
package main
import (
"encoding/json"
"fmt"
)
type Persona struct {
Nombre string `json:"nombre"`
Amigos []*Persona `json:"amigos"`
}
type PersonaSerializable struct {
Nombre string `json:"nombre"`
Amigos []string `json:"amigos"`
}
func convertirAPersonaSerializable(persona *Persona) PersonaSerializable {
amigosSerializables := make([]string, len(persona.Amigos))
for indice, amigo := range persona.Amigos {
amigosSerializables[indice] = amigo.Nombre
}
return PersonaSerializable{
Nombre: persona.Nombre,
Amigos: amigosSerializables,
}
}
func main() {
maria := &Persona{Nombre: "Maria"}
juan := &Persona{Nombre: "Juan"}
maria.Amigos = []*Persona{juan}
juan.Amigos = []*Persona{maria}
data, err := json.Marshal(convertirAPersonaSerializable(maria)) // ahora no habrá errores de bucle
if err != nil {
fmt.Println(err.Error())
panic(err)
}
fmt.Println(string(data))
}
当我们使用kude14执行代码时,此解决方案效果很好。这是一个强制代码,我们可以以另一种方式避免此问题,但这是我发生的最简单的方法来说明此错误。它纯粹是DIDA,希望您已经传输了错误以及如何纠正它。
结论
我们到达本文的结尾。我开始阅读“ 100次错误错误以及如何避免它们”后带来了错误和解决方案,本书的作者首先提出错误和不良实用性,然后为其提供的这些情况提供解决方案。因此,您决定使用更基本的方法来尝试相同的方法,以查看它的工作原理。我希望你喜欢它!
总而言之,Kude1和**Unmarshal()**
函数在与JSON合作时是至关重要的资源,但是需要具体的预防措施以确保其代码有效,安全和无错误。了解潜在的危险并遵循我在这里提到的一些实践,您可以有效地使用这些功能,而无需担心。
但是,请始终记住,(并且必须)我在此处尚未提及其他错误,因此您应始终咨询Encycothy/JSON软件包的官方文档,以获取有关这些功能正确使用这些功能的详细信息,示例,并始终与其他编程的人练习和交谈。
参考
Donovan,A。和Kernighhan,B。W.(2016)。转到语言程序。 Novatec编辑。
Enery/JSON软件包页面。可在以下网址提供:https://pkg.go.dev/encoding/json。
堆栈溢出。 Disponible EN:https://stackoverflow.com/questions/32708717/go-when-will-json-unmarshal-to-struct-return-error
Bahaha Shah中型文章 - “ Unmarshal的错误可能无法以您的思维方式起作用。”可在以下网址找到:https://baharbshah.medium.com/gos-unmarshal-errors-might-not-work-the-way-you-think-they-do-949f8fe15a09
验证器软件包页面。可用:https://pkg.go.dev/github.com/go-playground/validator/v10