Go ile boş bir yapı JSON'a nasıl sıralanmaz?


91

Bunun gibi bir yapım var:

type Result struct {
    Data       MyStruct  `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

Ancak MyStruct örneği tamamen boş olsa bile (yani, tüm değerler varsayılandır), şu şekilde serileştiriliyor:

"data":{}

Bunu biliyorum kodlama / json dokümanlar belirtmek alanlardır "boş":

false, 0, herhangi bir sıfır işaretçisi veya arabirim değeri ve sıfır uzunluktaki herhangi bir dizi, dilim, harita veya dize

ancak tüm boş / varsayılan değerlere sahip bir yapı düşünülmeden. Tüm alanları da ile etiketlenir omitempty, ancak bunun bir etkisi yoktur.

Nasıl için JSON paketi alabilirsiniz değil boş bir yapı benim alanını sıralamakta?

Yanıtlar:


142

Dokümanların dediği gibi, "herhangi bir sıfır işaretçisi." - yapıyı bir gösterici yapın. İşaretçilerin açık "boş" değerleri vardır:nil .

Düzeltme - türü bir struct işaretçi alanıyla tanımlayın :

type Result struct {
    Data       *MyStruct `json:"data,omitempty"`
    Status     string    `json:"status,omitempty"`
    Reason     string    `json:"reason,omitempty"`
}

Sonra bunun gibi bir değer:

result := Result{}

Sıralayacağı gibi:

{}

Açıklama: Dikkat edin *MyStruct Tip tanımımızdaki edin. JSON serileştirme bir işaretçi olup olmadığı umurunda değil - bu bir çalışma zamanı detayıdır. Dolayısıyla, yapı alanlarını işaretçiler haline getirmenin yalnızca derleme ve çalışma zamanı için etkileri vardır).

Alan türünü olarak olarak MyStructdeğiştirirseniz *MyStruct, doldurmak için değerleri yapılandırmak için işaretçilere ihtiyacınız olacaktır, örneğin:

Data: &MyStruct{ /* values */ }

2
Çok
yaşa

@Matt, &MyStruct{ /* values */ }bunun sıfır işaretçi olarak sayıldığından emin misin ? Değer sıfır değil.
Shuzheng

@Matt Bu varsayılan davranışı yapmak mümkün mü? Her zaman boş olmak istiyorum. (temelde etiketi tüm yapıların her alanında kullanmayın)
Mohit Singh

18

@Chakrit bir yorumda belirtildiği gibi, uygulayarak çalışmalarına bu alamayan json.Marshalerüzerinde MyStructve kullanımları çok daha fazla iş olabilir her yapı üzerinde özel JSON manevra fonksiyonunu uygulamak. Ekstra çalışmaya değip değmeyeceği veya JSON'nuzda boş yapılarla yaşamaya hazır olup olmadığınıza ilişkin kullanım durumunuza gerçekten bağlıdır, ancak işte benim uyguladığım kalıp Result:

type Result struct {
    Data       MyStruct
    Status     string   
    Reason     string    
}

func (r Result) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct {
        Data     *MyStruct   `json:"data,omitempty"`
        Status   string      `json:"status,omitempty"`
        Reason   string      `json:"reason,omitempty"`
    }{
        Data:   &r.Data,
        Status: r.Status,
        Reason: r.Reason,
    })        
}

func (r *Result) UnmarshalJSON(b []byte) error {
    decoded := new(struct {
        Data     *MyStruct   `json:"data,omitempty"`
        Status   string      `json:"status,omitempty"`
        Reason   string      `json:"reason,omitempty"`
    })
    err := json.Unmarshal(b, decoded)
    if err == nil {
        r.Data = decoded.Data
        r.Status = decoded.Status
        r.Reason = decoded.Reason
    }
    return err
}

Birçok alan içeren devasa yapılarınız varsa, bu sıkıcı hale gelebilir, özellikle bir yapının uygulamasını daha sonra değiştirmek, ancak tüm jsonpaketi ihtiyaçlarınıza uyacak şekilde yeniden yazmaktan (iyi bir fikir değil), hemen hemen bunu elde etmek için düşünebileceğim tek yol budur. bu hala işaretçi olmayan bir şekilde yapılırMyStruct orada .

Ayrıca, satır içi yapılar kullanmak zorunda değilsiniz, adlandırılmış yapılar oluşturabilirsiniz. Yine de kod tamamlama ile LiteIDE kullanıyorum, bu yüzden dağınıklığı önlemek için satır içi yapmayı tercih ediyorum.


9

Data başlatılmış bir yapı olduğundan boş kabul edilmez çünkü encoding/json yapının içindeki alanlara değil, yalnızca anlık değere bakar.

Maalesef dönen nildan json.Marhslerçalışmıyor şu anda:

func (_ MyStruct) MarshalJSON() ([]byte, error) {
    if empty {
        return nil, nil // unexpected end of JSON input
    }
    // ...
}

ResultBir görevli de verebilirsin , ama bu çabaya değmez.

Matt'in önerdiği gibi tek seçenek, Databir işaretçi yapmak ve değeri olarak ayarlamaktır nil.


1
Yapının alt alanlarını neden kontrol encoding/json edemediğini anlamıyorum . Çok verimli olmaz, evet. Ama kesinlikle imkansız değil.
nemo

@nemo Demek istediğini anladım, ifadeyi değiştirdim. Bunu yapmaz çünkü verimli olmaz. Yine de, duruma göre yapılabilir json.Marshaler.
Luke

2
Öyle değil mümkün Hava karar veya MyStructboş bir uygulayarak json.Marshalerüzerinde MyStructkendisi. Kanıt: play.golang.org/p/UEC8A3JGvx
chakrit

Bunu yapmak için, çok sakıncalı olabilecek json.Marshalerkapsayıcı Resulttürün kendisine uygulamanız gerekir.
chakrit

4

4 yıldan uzun süredir aktif olan bu özellik için olağanüstü bir Golang önerisi var , bu nedenle bu noktada, yakında herhangi bir zamanda standart kitaplığa girmeyeceğini varsaymak güvenli. @Matt belirttiği gibi, geleneksel yaklaşım dönüştürmek olduğunu yapılar için işaretçiler-to-yapılar . Bu yaklaşım uygulanabilir değilse (veya pratik değilse), o zaman bir alternatif, sıfır değerli yapıların atlanmasını destekleyen alternatif bir json kodlayıcı kullanmaktır. .

Golang json kitaplığının bir aynasını oluşturdum (Etiket uygulandığında sıfır değerli yapıların çıkarılmasıiçin ek destekle clarketm / json )omitempty. Bu kütüphane algılar zeroness popüler YAML kodlayıcı için benzer şekilde go-YAML tarafından yinelemeli kontrol kamu yapı alanları .

Örneğin

$ go get -u "github.com/clarketm/json"
import (
    "fmt"
    "github.com/clarketm/json" // drop-in replacement for `encoding/json`
)

type Result struct {
    Data   MyStruct `json:"data,omitempty"`
    Status string   `json:"status,omitempty"`
    Reason string   `json:"reason,omitempty"`
}

j, _ := json.Marshal(&Result{
    Status: "204",
    Reason: "No Content",
})

fmt.Println(string(j))
// Note: `data` is omitted from the resultant json.
{
  "status": "204"
  "reason": "No Content"
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.