Unmarshaling verschachtelter JSON-Objekte

122

Es gibt ein paar Fragen zu diesem Thema, aber keine scheint meinen Fall abzudecken, daher erstelle ich eine neue.

Ich habe JSON wie folgt:

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

Gibt es eine Möglichkeit, die Eigenschaft der verschachtelten Leiste zu entfernen und sie direkt einer Struktureigenschaft zuzuweisen, ohne eine verschachtelte Struktur zu erstellen?

Die Lösung, die ich gerade übernehme, ist die folgende:

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

Dies ist eine vereinfachte Version, bitte ignorieren Sie die Ausführlichkeit. Wie Sie sehen, möchte ich den Wert analysieren und zuweisen können

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

Ich habe Leute gesehen, die eine Karte benutzt haben, aber das ist nicht mein Fall. Der Inhalt von foo(bei dem es sich um ein großes Objekt handelt) ist mir im Grunde genommen egal , mit Ausnahme einiger spezifischer Elemente.

Was ist in diesem Fall der richtige Ansatz? Ich bin nicht auf der Suche nach seltsamen Hacks. Wenn dies der richtige Weg ist, bin ich damit einverstanden.

Simone Carletti
quelle

Antworten:

67

Gibt es eine Möglichkeit, die Eigenschaft der verschachtelten Leiste zu entfernen und sie direkt einer Struktureigenschaft zuzuweisen, ohne eine verschachtelte Struktur zu erstellen?

Nein, encoding / json kann den Trick mit "> some> deep> childnode" nicht ausführen, wie es encoding / xml kann. Verschachtelte Strukturen sind der richtige Weg.

Volker
quelle
1
Warum unterscheidet sich das von encoding / xml?
Caleb Hearth
1
@CalebThompson Die Struktur für XML und JSON ist völlig unterschiedlich, auch wenn die einfachen Fälle gleich aussehen. Der Inhalt eines XML-Tags ist ein bisschen: (Eine geordnete Karte von Unter-Tags ODER Text) UND eine ungeordnete Karte von Attributen. JSON ähnelt viel eher einer Go-Struktur. Das Zuordnen von JSON zu Strukturen ist also viel einfacher: Modellieren Sie die Struktur einfach nach Ihrem JSON.
Volker
In meinem Fall ist die Struktur von JSON nicht wirklich festgelegt, sodass ich eine Struktur erstellen kann. Wenn ich sie mithilfe der Zuordnung der Schnittstelle [string] {} analysiere, treten Probleme mit verschachtelten Elementen auf. Was kann getan werden?
viveksinghggits
Aber warum können wir nicht die Struktur innerhalb der Struktur aufheben?
Vitaly Zdanevich
29

Wie Volker bereits erwähnt hat, sind verschachtelte Strukturen der richtige Weg. Wenn Sie jedoch wirklich keine verschachtelten Strukturen möchten, können Sie die UnmarshalJSON-Funktion überschreiben.

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
}

Bitte ignorieren Sie die Tatsache, dass ich keinen richtigen Fehler zurücksende. Ich habe das der Einfachheit halber weggelassen.

UPDATE: Richtiges Abrufen des Werts "more".

rexposadas
quelle
3
Ich bekomme & {FooBar: 1 FooBaz: 2 More:}. "Text" fehlt
Guy Segev
@GuySegev Ich habe meine Antwort aktualisiert, um dieses Problem zu beheben. Vielen Dank für den Hinweis.
Rexposadas
22

Dies ist ein Beispiel für das Aufheben der Bereitstellung von JSON-Antworten vom Safebrowsing v4-API-sbserver-Proxyserver: 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)
}
Franke
quelle
2
Vielen Dank, dass Sie gezeigt haben, dass json.Unmarshal komplexe, tief verschachtelte json-Daten entfernen kann . Mein Problem war, dass ich JSON aus einer Datei las und am Ende keine Auffüllung hatte. Ich bin froh, dass du das geteilt hast!
Rohanthewiz
12

Ja. Mit gjson müssen Sie jetzt nur noch Folgendes tun:

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

barkönnte eine struct-Eigenschaft sein, wenn Sie möchten. Auch keine Karten.

Bögen wechseln
quelle
1
Fastjson erlaubt auch den gleichen Trick: fastjson.GetString(json, "foo", "bar")
Valyala
9

Was ist mit anonymen Feldern? Ich bin nicht sicher, ob dies eine "verschachtelte Struktur" darstellt, aber es ist sauberer als eine verschachtelte Strukturdeklaration. Was ist, wenn Sie das verschachtelte Element an anderer Stelle wiederverwenden möchten?

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

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

Weisen jsonSie struct die Werte von verschachtelt zu, bis Sie den zugrunde liegenden Typ der JSON-Schlüssel kennen: -

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)
}
Himanshu
quelle
0

Ich habe an so etwas gearbeitet. Arbeitet aber nur mit Strukturen, die aus Proto generiert wurden. https://github.com/flowup-labs/grpc-utils

in deinem 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"];
}

Dann wird Ihre Ausgabe sein

{
"lastname": "Three",
"name": {
    "firstname": "One",
    "inside": {
        "a": {
            "b": {
                "c": "goo"
            }
        }
    },
    "lastname": "Two"
},
"opt1": "var"
}
Vladan Ryšavý
quelle
2
Fügen Sie einige Zeilen hinzu, um zu erklären, wie dies die Frage beantwortet. Wenn das Repo gelöscht wird, ist in der Antwort kein Wert mehr vorhanden.
Ubercool
Ich glaube nicht, dass er zurückkommt, Kumpels.
DevX
-1

Durch die Kombination von Map und Struct können Sie verschachtelte JSON-Objekte aufheben, bei denen der Schlüssel dynamisch ist. => map [string]

Zum Beispiel: 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
  }
}

Gehen Sie zur Anwendung

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

Der dynamische Schlüssel im Hash behandelt eine Zeichenfolge, und das verschachtelte Objekt wird durch eine Struktur dargestellt.

jvmvik
quelle
3
das scheint unvollständig. raw ist unbenutzt
buildmaestro