Usuwanie zagnieżdżonych obiektów JSON

122

Istnieją a kilka pytań na temat , ale żaden z nich nie wydają się pokrywać moim przypadku, więc tworzę nowy.

Mam JSON jak poniżej:

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

Czy istnieje sposób, aby cofnąć zagnieżdżoną właściwość bar i przypisać ją bezpośrednio do właściwości struct bez tworzenia zagnieżdżonej struktury?

Rozwiązanie, które teraz stosuję, jest następujące:

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

To jest wersja uproszczona, zignoruj ​​szczegółowość. Jak widać, chciałbym móc przeanalizować i przypisać wartość

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

Widziałem ludzi używających mapy, ale to nie moja sprawa. W zasadzie nie obchodzi mnie zawartość foo(który jest dużym obiektem), z wyjątkiem kilku konkretnych elementów.

Jakie jest prawidłowe podejście w tym przypadku? Nie szukam dziwnych hacków, więc jeśli tak jest, nie mam z tym problemu.

Simone Carletti
źródło

Odpowiedzi:

67

Czy istnieje sposób, aby cofnąć zagnieżdżoną właściwość bar i przypisać ją bezpośrednio do właściwości struct bez tworzenia zagnieżdżonej struktury?

Nie, kodowanie / json nie może załatwić sprawy z "> jakimś> głębokim> węzłem potomnym", tak jak potrafi to zrobić kodowanie / xml. Struktury zagnieżdżone to droga do zrobienia.

Volker
źródło
1
Dlaczego to różni się od encoding / xml?
Caleb Hearth
1
@CalebThompson Struktura XML i JSON jest zupełnie inna, nawet jeśli proste przypadki wyglądają podobnie. Treść znacznika XML to coś w rodzaju: (uporządkowana mapa tagów podrzędnych LUB tekst) ORAZ nieuporządkowana mapa atrybutów. JSON jest bardziej podobny do struktury Go. Zatem mapowanie JSON do struktur jest znacznie prostsze: po prostu zamodeluj strukturę po JSON.
Volker
w moim przypadku struktura JSON nie jest tak naprawdę ustalona, ​​więc mogę utworzyć strukturę, a kiedy parsuję ją za pomocą mapy [string] interface {}, mam problemy z zagnieżdżonymi elementami. Co można zrobić.?
viveksinghggits
Ale dlaczego nie możemy nie narzekać na strukturowanie wewnątrz struktury?
Witalij Zdanevich
29

Tak jak wspomniał Volker, najlepszym rozwiązaniem są struktury zagnieżdżone. Ale jeśli naprawdę nie chcesz zagnieżdżonych struktur, możesz nadpisać funkcję 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
}

Proszę zignorować fakt, że nie zwracam właściwego błędu. Pominąłem to dla uproszczenia.

UPDATE: Prawidłowe pobieranie „większej” wartości.

rexposadas
źródło
3
Otrzymuję & {FooBar: 1 FooBaz: 2 Więcej:}. Brakuje „tekstu”
Guy Segev,
@GuySegev Poszedłem dalej i zaktualizowałem moją odpowiedź, aby rozwiązać ten problem. Dzięki za zwrócenie uwagi.
rexposadas
22

To jest przykład tego, jak usunąć odpowiedzi JSON z serwera proxy sbserver 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)
}
Franke
źródło
2
Dzięki za pokazanie, że json.Unmarshal może cofnąć maskowanie złożonych, głęboko zagnieżdżonych danych JSON . Mój problem polegał na tym, że czytałem JSON z pliku i skończyło się na wypełnieniu zerowym. Cieszę się, że to udostępniłeś!
Rohanthewiz
12

Tak. Dzięki gjson wszystko, co musisz teraz zrobić, to:

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

barjeśli chcesz, może to być właściwość struct. Nie ma też map.

zmiana łuków
źródło
1
fastjson pozwala również na tę samą sztuczkę: fastjson.GetString(json, "foo", "bar")
valyala
9

A co z anonimowymi polami? Nie jestem pewien, czy będzie to stanowić „zagnieżdżoną strukturę”, ale jest to czystsze niż deklaracja zagnieżdżonej struktury. A co jeśli chcesz ponownie użyć zagnieżdżonego elementu w innym miejscu?

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

type BaseElement struct {
    NestedElement `json:"bar"`
}
Rixarn
źródło
1

Przypisz wartości zagnieżdżone jsondo struct, dopóki nie poznasz podstawowego typu kluczy 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)
}
Himanshu
źródło
0

Pracowałem nad czymś takim. Ale działa tylko ze strukturami wygenerowanymi z proto. https://github.com/flowup-labs/grpc-utils

w twoim 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"];
}

Wtedy twoje wyjście będzie

{
"lastname": "Three",
"name": {
    "firstname": "One",
    "inside": {
        "a": {
            "b": {
                "c": "goo"
            }
        }
    },
    "lastname": "Two"
},
"opt1": "var"
}
Vladan Ryšavý
źródło
2
Dodaj kilka wierszy, aby wyjaśnić, jak to odpowiada na pytanie. Jeśli repozytorium zostanie usunięte, w odpowiedzi nie pozostanie żadna wartość.
Ubercool
Nie sądzę, żeby wrócił, koledzy.
DevX,
-1

Połączenie map i struct umożliwia usuwanie zagnieżdżonych obiektów JSON, w których klucz jest dynamiczny. => mapa [ciąg]

Na przykład: 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
  }
}

Aplikacja Go

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

Klucz dynamiczny w skrócie obsługuje ciąg, a zagnieżdżony obiekt jest reprezentowany przez strukturę.

jvmvik
źródło
3
to wydaje się niekompletne. surowy jest nieużywany
buildmaestro