Tengo el siguiente json:

{
    "app": {
        "name": "name-of-app",
        "version" 1
    },
    "items": [
        {
            "type": "type-of-item",
            "inputs": {
                "input1": "value1"
            }
        }
    ]
}

El cambio items[0].inputs basado en el items[0].type.

Sabiendo eso, ¿hay alguna manera de mantener el campo inputs como una cadena? La idea es usar type para llamar al controlador derecho pasando el inputs, y allí analizaría la cadena inputs usando la estructura correcta.

Ejemplo:

package main

import (
    "fmt"
    "encoding/json"
)

type Configuration struct {
    App   App `json:"app"`
    Items []Item `json:"items"`
}

type App struct {
    Name    string `json:"name"`
    Version int    `json:"version"`
}

type Item struct {
    Type string `json:"type"`
    // What to put here to mantain the field a string so I can Unmarshal later?
    // Inputs string
}

var myJson = `
{
    "app": {
        "name": "name-of-app",
        "version": 1
    },
    "items": [
        {
            "type": "type-of-item",
            "inputs": {
                "input1": "value1"
            }
        }
    ]
}
`

func main() {
    data := Configuration{}
    json.Unmarshal([]byte(myJson), &data)

    fmt.Println("done!", data)
    // Loop through data.Items and use the type to define what to call, and pass inputs
    // as argument
}

Gracias de antemano.

3
Amanda Ferrari 10 sep. 2018 a las 05:39

3 respuestas

La mejor respuesta

Utilice json.RawMessage para obtener el texto JSON sin procesar del campo inputs:

type Item struct {
    Type   string `json:"type"`
    Inputs json.RawMessage
}

Úselo así:

var data Configuration
if err := json.Unmarshal([]byte(myJson), &data); err != nil {
    // handle error
}

// Loop over items and unmarshal items.Inputs to Go type specific
// to each input type.    
for _, item := range data.Items {
    switch item.Type {
    case "type-of-item":
        var v struct{ Input1 string }
        if err := json.Unmarshal(item.Inputs, &v); err != nil {
            // handle error
        }
        fmt.Printf("%s has value %+v\n", item.Type, v)

    }
}

Ejecútelo en el patio de recreo.

16
Cerise Limón 10 sep. 2018 a las 03:18

Prueba gjson, súper simple, no tienes que desarmar todo. puede tomar los bytes y extraer un campo específico. https://github.com/tidwall/gjson

    // Get string (has string, int, bool parsing)
    someField := gjson.ParseBytes(b).Get("some_field.some_nested_field").Str
    // Other types
    v, ok := gjson.ParseBytes(b).Get("some_other_field").Value().(map[string]string)
2
Earl Huskey 11 sep. 2018 a las 17:16

Para ser justos, Go analizaría parcialmente si definiera una estructura parcial. Citando la documentación ( https://blog.golang.org/json-and-go):

¿Cómo identifica Unmarshal los campos en los que almacenar los datos decodificados? Para una clave JSON "Foo" dada, Unmarshal buscará en los campos de la estructura de destino para encontrar (en orden de preferencia):

An exported field with a tag of "Foo" (see the Go spec for more on struct tags),

An exported field named "Foo", or

An exported field named "FOO" or "FoO" or some other case-insensitive match of "Foo".

¿Qué sucede cuando la estructura de los datos JSON no coincide exactamente con el tipo Go?

b := []byte(`{"Name":"Bob","Food":"Pickle"}`)
var m Message
err := json.Unmarshal(b, &m)

Unmarshal decodificará solo los campos que puede encontrar en el tipo de destino. En este caso, solo se rellenará el campo Nombre de m y se ignorará el campo Alimento. Este comportamiento es particularmente útil cuando desea elegir solo unos pocos campos específicos de un blob JSON grande. También significa que los campos no exportados en la estructura de destino no se verán afectados por Unmarshal.

1
pablosan 6 dic. 2018 a las 16:44