2012-06-20 12 views
40

¿Existe algún motivo técnico por el que los campos no exportados no estén incluidos en la codificación/json? De lo contrario, y se trata de una decisión arbitraria, ¿podría existir una opción de puerta trasera adicional (por ejemplo, "+") para incluir aunque no se haya exportado?JSON y tratar con campos no exportados

Exigir código de cliente para exportar para obtener esta funcionalidad parece desafortunado, especialmente si minúsculas proporciona encapsulación o si la decisión de organizar las estructuras llega mucho más tarde que el diseño de las mismas.

¿Cómo están lidiando las personas con esto? Solo exportar todo?

Además, no exportar nombres de campo hace que sea difícil seguir los modismos sugeridos. Creo que si una estructura X tiene el campo Y, no puede tener un método de acceso Y(). Si desea proporcionar acceso a la interfaz Y usted tiene que llegar a un nuevo nombre para el comprador y no importa lo que se obtiene algo no-idiomática según http://golang.org/doc/effective_go.html#Getters

Respuesta

64

Hay una razón técnica. La biblioteca json no tiene la capacidad de ver campos usando reflect a menos que se exporten. Un paquete solo puede ver los campos no exportados de tipos dentro de su propio paquete

Para resolver su problema, lo que puede hacer es crear un tipo no exportado con los campos exportados. Json se convertirá en un tipo no exportado si se le transfiere sin problemas, pero no se mostrará en los documentos de la API. Luego puede hacer un tipo exportado que incruste el tipo no exportado. Este tipo exportado necesitaría entonces métodos para implementar las interfaces json.Marshaler y json.Unmarshaler.

Nota: no se ha probado todo el código y es posible que ni siquiera se compile.

type jsonData struct { 
    Field1 string 
    Field2 string 
} 

type JsonData struct { 
    jsonData 
} 

// Implement json.Unmarshaller 
func (d *JsonData) UnmarshalJSON(b []byte) error { 
    return json.Unmarshal(b, &d.jsonData) 
} 

// Getter 
func (d *JsonData) Field1() string { 
    return d.jsonData.Field1 
} 
+0

Para el registro, tuve que Unmarshall usando "json.Unmarshal (b, & d.jsonData)". ¿Hice algo mal, o es eso esperado? – Derek

+0

@Derek, gracias, actualicé mi respuesta. Como dije, el código no fue probado. Aparentemente también olvidé una declaración de devolución en mi método 'UnmarshalJSON()'. Yo arreglé esto también. –

+3

Llego un poco tarde al espectáculo, pero ... mientras que lo anterior funciona, se exportan Field1 y Field2 _. Puede leer y escribir Campo1 y Campo2 de JsonData (con una J mayúscula) fuera de ese paquete. Entonces, aunque esto es genial en teoría, en realidad no hace nada diferente a exportar tanto el tipo como los campos. – davidjosepha

41

La respuesta de Stephen ha sido completa. Como acotación al margen, si todo lo que realmente quiere es teclas minúsculas en su JSON, se puede especificar manualmente el nombre de la clave de la siguiente manera:

type Whatever struct { 
    SomeField int `json:"some_field"` 
} 

De esa manera, el cálculo de referencias de un Lo que produce la tecla "some_field" para el SomeField campo (en lugar de tener "SomeField" en tu json).

Si está establecido como muerto para mantener los campos no exportados, también puede implementar la interfaz json.Marshaler definiendo un método con la firma MarshalJSON() ([]byte, error). Una forma de hacer esto es utilizar una estructura literal que simplemente ha exportado versiones de los campos no exportadas, así:

type Whatever struct { 
    someField int 
} 

func (w Whatever) MarshalJSON() ([]byte, error) { 
    return json.Marshal(struct{ 
     SomeField int `json:"some_field"` 
    }{ 
     SomeField: w.someField, 
    }) 
} 

que puede ser un poco engorroso, por lo que también se puede utilizar un map[string]interface{} si lo prefiere:

func (w Whatever) MarshalJSON() ([]byte, error) { 
    return json.Marshal(map[string]interface{}{ 
     "some_field": w.SomeField, 
    }) 
} 

Sin embargo debe tenerse en cuenta que el cálculo de referencias interface{} tiene algunas advertencias y puede hacer cosas como mariscal uint64 a un flotador, causando una pérdida de precisión. (todo código no probado)

+0

Técnicamente, TODOS los números en Javascript son flotadores. –

+16

JSON no es JavaScript. La especificación JSON simplemente indica qué caracteres son aceptables, no qué rangos numéricos son válidos. Un número como 876234958273645982736459827346598237465923847561203947812435968234659827346 sigue siendo válido en JSON, incluso si no puede ser entendido por JavaScript. Para un ejemplo del mundo real, la API de Twitter representa los ID de tweet como enteros sin signo de 64 bits, que no es válido en JavaScript, pero es JSON válido. – jorelli

+1

Actualmente, todo es cierto, pero el nombre sigue siendo JSON, que históricamente significa "notación de objetos de JavaScript". Uno de esos pequeños bits sorprendentes que hacen que quieras cambiarle el nombre a NJSON (NewJSON o más bien NotJavaScript) :) – quetzalcoatl

Cuestiones relacionadas