Unmarshaling di oggetti JSON nidificati


122

Ci sono un paio di domande sul tema , ma nessuno di loro sembra per coprire il mio caso, quindi sto creando una nuova.

Ho JSON come il seguente:

{"foo":{ "bar": "1", "baz": "2" }, "more": "text"}

Esiste un modo per annullare il marshalling della proprietà della barra nidificata e assegnarla direttamente a una proprietà struct senza creare una struttura nidificata?

La soluzione che sto adottando in questo momento è la seguente:

type Foo struct {
    More String `json:"more"`
    Foo  struct {
        Bar string `json:"bar"`
        Baz string `json:"baz"`
    } `json:"foo"`
    //  FooBar  string `json:"foo.bar"`
}

Questa è una versione semplificata, ignora la verbosità. Come puoi vedere, mi piacerebbe essere in grado di analizzare e assegnare il valore a

//  FooBar  string `json:"foo.bar"`

Ho visto persone usare una mappa, ma non è il mio caso. Fondamentalmente non mi interessa il contenuto di foo(che è un oggetto di grandi dimensioni), ad eccezione di alcuni elementi specifici.

Qual è l'approccio corretto in questo caso? Non sto cercando strani hack, quindi se questa è la strada da percorrere, mi sta bene.

Risposte:


67

Esiste un modo per annullare il marshalling della proprietà della barra nidificata e assegnarla direttamente a una proprietà struct senza creare una struttura nidificata?

No, encoding / json non può fare il trucco con "> some> deep> childnode" come può fare encoding / xml. Gli struct annidati sono la strada da percorrere.


1
Perché è diverso da encoding / xml?
Caleb Hearth

1
@CalebThompson La struttura per XML e JSON è completamente diversa, anche se i casi semplici si assomigliano. Il contenuto di un tag XML è un po ': (una mappa ordinata di sotto-tag O testo) E una mappa non ordinata di attributi. JSON è molto più simile a una struttura Go. Quindi mappare JSON alle strutture è molto più semplice: basta modellare la struttura dopo il tuo JSON.
Volker

nel mio caso la struttura di JSON non è effettivamente decisa, quindi posso creare una struttura e quando la analizzo usando la mappa dell'interfaccia [stringa] {}, ho problemi con gli elementi nidificati. Cosa si può fare.?
viveksinghggits

Ma perché non possiamo smontare la struttura all'interno della struttura?
Vitaly Zdanevich

29

Come quello che ha menzionato Volker, le strutture annidate sono la strada da percorrere. Ma se davvero non vuoi strutture annidate, puoi sovrascrivere la funzione UnmarshalJSON.

https://play.golang.org/p/dqn5UdqFfJt

type A struct {
    FooBar string // takes foo.bar
    FooBaz string // takes foo.baz
    More   string 
}

func (a *A) UnmarshalJSON(b []byte) error {

    var f interface{}
    json.Unmarshal(b, &f)

    m := f.(map[string]interface{})

    foomap := m["foo"]
    v := foomap.(map[string]interface{})

    a.FooBar = v["bar"].(string)
    a.FooBaz = v["baz"].(string)
    a.More = m["more"].(string)

    return nil
}

Si prega di ignorare il fatto che non sto restituendo un errore corretto. L'ho lasciato fuori per semplicità.

AGGIORNAMENTO: Recupero corretto del valore "più".


3
Ricevo & {FooBar: 1 FooBaz: 2 Altro:}. "Testo" mancante
Guy Segev

@GuySegev Sono andato avanti e ho aggiornato la mia risposta per risolvere il problema. Grazie per la segnalazione.
rexposadas

22

Questo è un esempio di come annullare il marshalling delle risposte JSON dal server proxy sbserver dell'API Safebrowsing v4: https://play.golang.org/p/4rGB5da0Lt

// this example shows how to unmarshall JSON requests from the Safebrowsing v4 sbserver
package main

import (
    "fmt"
    "log"
    "encoding/json"
)

// response from sbserver POST request
type Results struct {
    Matches []Match     
}

// nested within sbserver response
type Match struct {
    ThreatType string 
    PlatformType string 
    ThreatEntryType string 
    Threat struct {
        URL string
    }
}

func main() {
    fmt.Println("Hello, playground")

    // sample POST request
    //   curl -X POST -H 'Content-Type: application/json' 
    // -d '{"threatInfo": {"threatEntries": [{"url": "http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}]}}' 
    // http://127.0.0.1:8080/v4/threatMatches:find

    // sample JSON response
    jsonResponse := `{"matches":[{"threatType":"MALWARE","platformType":"ANY_PLATFORM","threatEntryType":"URL","threat":{"url":"http://testsafebrowsing.appspot.com/apiv4/ANY_PLATFORM/MALWARE/URL/"}}]}`

    res := &Results{}
    err := json.Unmarshal([]byte(jsonResponse), res)
        if(err!=nil) {
            log.Fatal(err)
        }

    fmt.Printf("%v\n",res)
    fmt.Printf("\tThreat Type: %s\n",res.Matches[0].ThreatType)
    fmt.Printf("\tPlatform Type: %s\n",res.Matches[0].PlatformType)
    fmt.Printf("\tThreat Entry Type: %s\n",res.Matches[0].ThreatEntryType)
    fmt.Printf("\tURL: %s\n",res.Matches[0].Threat.URL)
}

2
Grazie per aver dimostrato che json.Unmarshal può annullare il marshalling di dati json complessi e profondamente annidati. Il mio problema era che stavo leggendo JSON da un file e ho finito con un riempimento zero. Sono contento che tu abbia condiviso questo!
Rohanthewiz

12

Sì. Con gjson tutto ciò che devi fare ora è:

bar := gjson.Get(json, "foo.bar")

barpotrebbe essere una proprietà struct, se lo desideri. Inoltre, nessuna mappa.


1
Fastjson permette anche lo stesso trucco: fastjson.GetString(json, "foo", "bar")
valyala

9

E i campi anonimi? Non sono sicuro che ciò costituirà una "struttura nidificata" ma è più pulita di avere una dichiarazione di struttura nidificata. E se volessi riutilizzare l'elemento nidificato altrove?

type NestedElement struct{
    someNumber int `json:"number"`
    someString string `json:"string"`
}

type BaseElement struct {
    NestedElement `json:"bar"`
}

1

Assegna i valori di annidato jsona struct finché non conosci il tipo sottostante di chiavi json: -

package main

import (
    "encoding/json"
    "fmt"
)

// Object
type Object struct {
    Foo map[string]map[string]string `json:"foo"`
    More string `json:"more"`
}

func main(){
    someJSONString := []byte(`{"foo":{ "bar": "1", "baz": "2" }, "more": "text"}`)
    var obj Object
    err := json.Unmarshal(someJSONString, &obj)
    if err != nil{
        fmt.Println(err)
    }
    fmt.Println("jsonObj", obj)
}

0

Stavo lavorando a qualcosa di simile. Ma funziona solo con strutture generate da proto. https://github.com/flowup-labs/grpc-utils

nel tuo proto

message Msg {
  Firstname string = 1 [(gogoproto.jsontag) = "name.firstname"];
  PseudoFirstname string = 2 [(gogoproto.jsontag) = "lastname"];
  EmbedMsg = 3  [(gogoproto.nullable) = false, (gogoproto.embed) = true];
  Lastname string = 4 [(gogoproto.jsontag) = "name.lastname"];
  Inside string  = 5 [(gogoproto.jsontag) = "name.inside.a.b.c"];
}

message EmbedMsg{
   Opt1 string = 1 [(gogoproto.jsontag) = "opt1"];
}

Quindi il tuo output sarà

{
"lastname": "Three",
"name": {
    "firstname": "One",
    "inside": {
        "a": {
            "b": {
                "c": "goo"
            }
        }
    },
    "lastname": "Two"
},
"opt1": "var"
}

2
Aggiungi poche righe per spiegare come questo risponde alla domanda. Se il repository viene eliminato, non è rimasto alcun valore nella risposta.
Ubercool

Non credo che tornerà, amici.
DevX

-1

La combinazione di map e struct consente l'unmarshaling di oggetti JSON nidificati in cui la chiave è dinamica. => mappa [stringa]

Ad esempio: stock.json

{
  "MU": {
    "symbol": "MU",
    "title": "micro semiconductor",
    "share": 400,
    "purchase_price": 60.5,
    "target_price": 70
  },
  "LSCC":{
    "symbol": "LSCC",
    "title": "lattice semiconductor",
    "share": 200,
    "purchase_price": 20,
    "target_price": 30
  }
}

Vai all'applicazione

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "os"
)

type Stock struct {
    Symbol        string  `json:"symbol"`
    Title         string  `json:"title"`
    Share         int     `json:"share"`
    PurchasePrice float64 `json:"purchase_price"`
    TargetPrice   float64 `json:"target_price"`
}
type Account map[string]Stock

func main() {
    raw, err := ioutil.ReadFile("stock.json")
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    var account Account
    log.Println(account)
}

La chiave dinamica nell'hash è gestire una stringa e l'oggetto annidato è rappresentato da una struttura.


3
questo sembra incompleto. raw è inutilizzato
buildmaestro
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.