web-dev-qa-db-fra.com

Démêler les objets JSON imbriqués dans Golang

Il y a apequestions sur le sujet mais aucun ne semble couvrir mon cas, donc je suis créer un nouveau.

J'ai JSON comme ce qui suit:

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

Existe-t-il un moyen de disséminer la propriété de barre imbriquée et de l'affecter directement à une propriété de structure sans créer de structure imbriquée?

La solution que j'adopte actuellement est la suivante:

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

Ceci est une version simplifiée, veuillez ignorer la verbosité. Comme vous pouvez le constater, j'aimerais pouvoir analyser et attribuer la valeur à

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

J'ai vu des gens utiliser une carte, mais ce n'est pas mon cas. Fondamentalement, je ne me soucie pas du contenu de foo (qui est un objet volumineux), à l'exception de quelques éléments spécifiques.

Quelle est la bonne approche dans ce cas? Je ne cherche pas de hacks étranges, donc si c'est la voie à suivre, ça me convient.

95
Simone Carletti

Existe-t-il un moyen de disséminer la propriété de barre imbriquée et de l'affecter directement à une propriété de structure sans créer de structure imbriquée?

Non, encoding/json ne peut pas faire l'affaire avec "> some> deep> childnode" contrairement à encoding/xml. Les structures imbriquées sont la voie à suivre.

56
Volker

Comme ce que Volker a mentionné, les structures imbriquées sont la voie à suivre. Mais si vous réellement ne voulez pas de structures imbriquées, vous pouvez remplacer la fonction 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
}

Veuillez ignorer le fait que je ne renvoie pas une erreur appropriée. J'ai laissé cela pour plus de simplicité.

UPDATE: Récupération correcte de la valeur "more".

27
rexposadas

Voici un exemple de suppression des réponses JSON du serveur proxy sbserver de Safebrowsing v4 API: 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)
}
18
Franke

Oui. Avec gjson , tout ce que vous avez à faire maintenant est:

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

bar pourrait être une propriété de struct si vous voulez. En outre, pas de cartes.

9
changingrainbows

Qu'en est-il des champs anonymes? Je ne sais pas si cela constituera une "structure imbriquée", mais cela est plus propre qu'une déclaration de structure imbriquée. Que faire si vous souhaitez réutiliser l'élément imbriqué ailleurs?

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

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

Attribuez les valeurs de json imbriqué à struct jusqu'à ce que vous connaissiez le type de clés JSON sous-jacent: -

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)
}
1
Himanshu

Je travaillais sur quelque chose comme ça. Mais travaille uniquement avec des structures générées à partir de proto. https://github.com/flowup-labs/grpc-utils

dans ton 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"];
}

Ensuite, votre sortie sera

{
"lastname": "Three",
"name": {
    "firstname": "One",
    "inside": {
        "a": {
            "b": {
                "c": "goo"
            }
        }
    },
    "lastname": "Two"
},
"opt1": "var"
}
0
Vladan Ryšavý