convertir - bytes() python
Tomar una cadena JSON, desarmarla en una interfaz de mapa[cadena]{}, editarla y ordenarla en un byte[] parece más complicado de lo que debería ser (2)
No, esta no es la forma más correcta de manejar datos JSON estructurados en Go. En cambio, es mejor crear una "jerarquía de estructura" y desarmar su JSON en estructuras. P.ej
type Data struct {
Key1 string
Key2 struct {
C1Key1 string
}
Key3 []Key3
}
type Key3 struct {
C2Key1 struct {
C3Key1 string
}
}
Este enfoque:
-
le da más control sobre cómo sus datos serán (
json.Marshaler
)json.Marshaler
(a través de etiquetas de estructura e interfacesjson.Unmarshaler
yjson.Marshaler
) - te deshace de las aserciones tipo
- en cambio, le da más seguridad de tipo
- tiene un mejor rendimiento, ya que el acceso a la estructura es básicamente gratuito en comparación con el acceso al mapa.
Área de juegos: https://play.golang.org/p/9XIh8DX1Ms .
Estoy haciendo una manipulación JSON muy básica para aprender un poco de Go, y funciona, excepto que una cosa parece incorrecta, tengo que escribir una gran cantidad de
.(map[string]interface{})
y
.([]interface{})
para acceder entradas en el JSON, especialmente si son hijos de hijos de niños, etc.
Ver aquí (también en Go Playground: https://play.golang.org/p/Wd-pzHqTsU ):
package main
import (
"fmt"
"encoding/json"
)
func main() {
JSON := []byte(`{"key1":"val1","key2":{"c1key1":"c1val1"},"key3":[{"c2key1":{"c3key1":"c3val1"}}]}`)
fmt.Printf("%s/n", JSON)
var d map[string]interface{}
json.Unmarshal(JSON, &d)
fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"])
d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"] = "change1"
fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"])
JSON, _ = json.Marshal(d)
fmt.Printf("%s/n", JSON)
}
Que devuelve:
{"key1":"val1","key2":{"c1key1":"c1val1"},"key3":[{"c2key1":{"c3key1":"c3val1"}}]}
c3val1
change1
{"key1":"val1","key2":{"c1key1":"c1val1"},"key3":[{"c2key1":{"c3key1":"change1"}}]}
Ahora, en Python, solo accedo a la clave / valores directamente en lugar de definir el tipo de lo que estoy accediendo cada vez, es decir, en lugar de
fmt.Println(d["key3"].([]interface{})[0].(map[string]interface{})["c2key1"].(map[string]interface{})["c3key1"])
print d["key3"][0]["c2key1"]["c3key1"]
Ejemplo de Python:
import json
JSON = ''{"key3": [{"c2key1": {"c3key1": "c3val1"}}], "key2": {"c1key1": "c1val1"}, "key1": "val1"}''
print JSON
d = json.loads(JSON)
print d["key3"][0]["c2key1"]["c3key1"]
d["key3"][0]["c2key1"]["c3key1"] = "change1"
print d["key3"][0]["c2key1"]["c3key1"]
JSON = json.dumps(d)
print JSON
Entonces, ¿estoy haciendo esto bien en Go? Y si es así, ¿cuál es la razón de este diseño? O si no, ¿cómo debo hacerlo?
Prefacio:
Optimicé y mejoré la solución a continuación, y la
github.com/icza/dyno
como una biblioteca aquí:
github.com/icza/dyno
.
La forma más limpia sería crear tipos predefinidos (estructuras de
struct
) que modelen su JSON, y desarmar a un valor de ese tipo, y simplemente puede referirse a elementos usando
Selectors
(para tipos de
struct
) y
expresiones de Índice
(para mapas y sectores) .
Sin embargo, si su entrada no es de una estructura predefinida, le sugiero las siguientes 2 funciones auxiliares:
get()
y
set()
.
El primero accede (devuelve) un elemento arbitrario especificado por una ruta arbitraria (lista de claves del mapa de
string
y / o índices de corte
int
), el segundo cambia (establece) el valor especificado por una ruta arbitraria (las implementaciones de estas funciones auxiliares están en El final de la respuesta).
Solo tiene que incluir estas 2 funciones una vez en su proyecto / aplicación.
Y ahora, utilizando estos ayudantes, las tareas que desea realizar se vuelven simples (al igual que la solución de Python):
fmt.Println(get(d, "key3", 0, "c2key1", "c3key1"))
set("NEWVALUE", d, "key3", 0, "c2key1", "c3key1")
fmt.Println(get(d, "key3", 0, "c2key1", "c3key1"))
Salida:
change1
NEWVALUE
Prueba tu aplicación modificada en Go Playground .
Nota - Simplificación adicional:
Incluso puede guardar la ruta en una variable y reutilizarla para simplificar aún más el código anterior:
path := []interface{}{"key3", 0, "c2key1", "c3key1"}
fmt.Println(get(d, path...))
set("NEWVALUE", d, path...)
fmt.Println(get(d, path...))
Y las implementaciones de
get()
y
set()
están a continuación.
Nota: comprueba si la ruta es válida se omite.
Esta implementación utiliza
interruptores de tipo
:
func get(m interface{}, path ...interface{}) interface{} {
for _, p := range path {
switch idx := p.(type) {
case string:
m = m.(map[string]interface{})[idx]
case int:
m = m.([]interface{})[idx]
}
}
return m
}
func set(v interface{}, m interface{}, path ...interface{}) {
for i, p := range path {
last := i == len(path)-1
switch idx := p.(type) {
case string:
if last {
m.(map[string]interface{})[idx] = v
} else {
m = m.(map[string]interface{})[idx]
}
case int:
if last {
m.([]interface{})[idx] = v
} else {
m = m.([]interface{})[idx]
}
}
}
}