Membatalkan penyusunan objek JSON bertingkat

122

Ada sebuah beberapa pertanyaan pada topik namun tidak satupun dari mereka tampaknya untuk menutupi kasus saya, sehingga saya membuat yang baru.

Saya memiliki JSON seperti berikut:

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

Apakah ada cara untuk membatalkan penguraian properti nested bar dan menetapkannya langsung ke properti struct tanpa membuat struct bersarang?

Solusi yang saya adopsi saat ini adalah sebagai berikut:

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

Ini adalah versi yang disederhanakan, harap abaikan verbositasnya. Seperti yang Anda lihat, saya ingin dapat mengurai dan menetapkan nilainya

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

Saya telah melihat orang-orang menggunakan peta, tetapi itu bukan kasus saya. Saya pada dasarnya tidak peduli tentang konten foo(yang merupakan objek besar), kecuali untuk beberapa elemen tertentu.

Apa pendekatan yang benar dalam kasus ini? Saya tidak mencari peretasan aneh, jadi jika ini cara yang harus dilakukan, saya baik-baik saja dengan itu.

Simone Carletti
sumber

Jawaban:

67

Apakah ada cara untuk membatalkan penguraian properti nested bar dan menetapkannya langsung ke properti struct tanpa membuat struct bersarang?

Tidak, encoding / json tidak dapat melakukan trik dengan "> beberapa> deep> childnode" seperti yang dapat dilakukan encoding / xml. Struct bersarang adalah cara yang harus dilakukan.

Volker
sumber
1
Mengapa ini berbeda dengan encoding / xml?
Caleb Hearth
1
@CalebThompson Struktur XML dan JSON benar-benar berbeda, meskipun kasus sederhana terlihat sama. Isi dari tag XML adalah: (Peta berurutan dari sub-tag ATAU Teks) DAN peta atribut yang tidak berurutan. JSON lebih seperti struct Go. Jadi memetakan JSON ke struct jauh lebih sederhana: Cukup buat model struct setelah JSON Anda.
Volker
dalam kasus saya, struktur JSON tidak benar-benar diputuskan sehingga saya bisa membuat struct dan ketika saya mengurai menggunakan peta antarmuka [string] {}, saya mengalami masalah untuk elemen bersarang. Apa yang bisa dilakukan.?
viveksinghggits
Tapi mengapa kita tidak bisa membatalkan ke struct inside struct?
Vitaly Zdanevich
29

Seperti yang disebutkan Volker, struct bersarang adalah cara yang tepat. Tetapi jika Anda benar - benar tidak ingin struct bersarang, Anda dapat mengganti fungsi 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
}

Harap abaikan fakta bahwa saya tidak mengembalikan kesalahan yang semestinya. Saya meninggalkannya untuk kesederhanaan.

UPDATE: Mengambil nilai "lebih" dengan benar.

rexposadas.dll
sumber
3
Saya mendapatkan & {FooBar: 1 FooBaz: 2 More:}. "Teks" tidak ada
Guy Segev
@GuySegev Saya melanjutkan dan memperbarui jawaban saya untuk memperbaiki masalah itu. Terima kasih telah menunjukkannya.
rexposadas
22

Ini adalah contoh cara membatalkan respons JSON dari server 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
sumber
2
Terima kasih telah menunjukkan bahwa json.Unmarshal dapat mengurai data json bersarang dalam yang kompleks. Masalah saya adalah saya membaca JSON dari file dan berakhir dengan beberapa padding nol. Senang Anda membagikan ini!
Rohanthewiz
12

Iya. Dengan gjson, yang harus Anda lakukan sekarang adalah:

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

barbisa menjadi properti struct jika Anda mau. Juga, tidak ada peta.

mengubah busur
sumber
1
fastjson juga memungkinkan trik yang sama: fastjson.GetString(json, "foo", "bar")
valyala
9

Bagaimana dengan bidang anonim? Saya tidak yakin apakah itu akan membentuk "struct bersarang" tapi itu lebih bersih daripada memiliki deklarasi struct bersarang. Bagaimana jika Anda ingin menggunakan kembali elemen bersarang di tempat lain?

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

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

Tetapkan nilai bersarang jsonke struct sampai Anda mengetahui jenis kunci json yang mendasarinya: -

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
sumber
0

Saya sedang mengerjakan sesuatu seperti ini. Tetapi bekerja hanya dengan struktur yang dihasilkan dari proto. https://github.com/flowup-labs/grpc-utils

dalam proto Anda

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

Maka output Anda akan menjadi

{
"lastname": "Three",
"name": {
    "firstname": "One",
    "inside": {
        "a": {
            "b": {
                "c": "goo"
            }
        }
    },
    "lastname": "Two"
},
"opt1": "var"
}
Vladan Ryšavý
sumber
2
Tambahkan beberapa baris untuk menjelaskan bagaimana ini menjawab pertanyaan. Jika repo dihapus tidak ada nilai yang tersisa di jawabannya.
Ubercool
Saya tidak berpikir dia akan kembali, sobat.
DevX
-1

Menggabungkan peta dan struct memungkinkan unmarshaling objek JSON bertingkat di mana kuncinya dinamis. => peta [string]

Contoh: 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
  }
}

Buka aplikasi

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

Kunci dinamis dalam hash menangani string, dan objek bertingkat diwakili oleh sebuah struct.

jvmvik.dll
sumber
3
ini sepertinya tidak lengkap. mentah tidak digunakan
buildmaestro