จะไม่จัดโครงสร้างที่ว่างเปล่าให้เป็น JSON ด้วย Go ได้อย่างไร


91

ฉันมีโครงสร้างดังนี้:

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

แต่แม้ว่าอินสแตนซ์ของ MyStruct จะว่างเปล่าทั้งหมด (หมายความว่าค่าทั้งหมดเป็นค่าเริ่มต้น) แต่ก็ถูกทำให้เป็นอนุกรมเป็น:

"data":{}

ฉันรู้ว่าเอกสารการเข้ารหัส / jsonระบุว่าฟิลด์ "ว่าง" คือ:

เท็จ, 0, ตัวชี้ศูนย์หรือค่าอินเทอร์เฟซและอาร์เรย์ชิ้นส่วนแผนที่หรือสตริงความยาวศูนย์

แต่ไม่มีการพิจารณาโครงสร้างที่มีค่าว่าง / ค่าเริ่มต้นทั้งหมด ฟิลด์ทั้งหมดจะถูกแท็กด้วยomitemptyแต่จะไม่มีผลใด ๆ

ฉันจะทำให้แพคเกจ JSON ไม่แสดงฟิลด์ของฉันที่เป็นโครงสร้างว่างได้อย่างไร

คำตอบ:


142

ตามที่เอกสารกล่าวว่า "ตัวชี้ศูนย์ใด ๆ " - ทำให้โครงสร้างเป็นตัวชี้ ชี้มีเห็นได้ชัด "ว่างเปล่า" nilค่า:

แก้ไข - กำหนดประเภทด้วยฟิลด์ตัวชี้โครงสร้าง :

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

จากนั้นค่าดังนี้:

result := Result{}

จะจอมพลเป็น:

{}

คำอธิบาย: สังเกต*MyStructในนิยามประเภทของเรา การทำให้อนุกรม JSON ไม่สนใจว่าจะเป็นตัวชี้หรือไม่นั่นคือรายละเอียดรันไทม์ ดังนั้นการสร้างฟิลด์ struct ให้เป็นพอยน์เตอร์จึงมีผลในการคอมไพล์และรันไทม์เท่านั้น)

โปรดทราบว่าหากคุณเปลี่ยนประเภทเขตข้อมูลจากMyStructเป็น*MyStructคุณจะต้องใช้ตัวชี้ไปยังค่าโครงสร้างเพื่อเติมข้อมูลดังนี้:

Data: &MyStruct{ /* values */ }

2
อวยพรคุณแมทนี่คือสิ่งที่ฉันกำลังมองหา
Venkata SSKM Chaitanya

@ แมทแน่ใจ&MyStruct{ /* values */ }เหรอว่านับเป็นตัวชี้ศูนย์? ค่าไม่ใช่ศูนย์
Shuzheng

@Matt เป็นไปได้ไหมที่จะสร้างพฤติกรรมเริ่มต้นนี้ ฉันต้องการเว้นว่างไว้เสมอ (โดยทั่วไปไม่ใช้แท็กในแต่ละฟิลด์ของโครงสร้างทั้งหมด)
Mohit Singh

18

ในฐานะที่เป็น @chakrit กล่าวถึงในความคิดเห็นคุณจะไม่สามารถได้รับนี้ในการทำงานโดยการดำเนินการjson.Marshalerเกี่ยวกับการMyStructและการใช้ฟังก์ชั่นนายอำเภอ JSON ที่กำหนดเองในทุก struct ที่ใช้ก็สามารถจะทำงานมากขึ้น ขึ้นอยู่กับกรณีการใช้งานของคุณว่าคุ้มค่ากับการทำงานพิเศษหรือไม่หรือคุณพร้อมที่จะใช้งานกับโครงสร้างที่ว่างเปล่าใน JSON ของคุณหรือไม่ แต่นี่คือรูปแบบที่ฉันใช้กับ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
}

หากคุณมีโครงสร้างขนาดใหญ่ที่มีหลายฟิลด์สิ่งนี้อาจเป็นเรื่องน่าเบื่อโดยเฉพาะอย่างยิ่งการเปลี่ยนการใช้งานโครงสร้างในภายหลัง แต่ไม่สามารถเขียนjsonแพ็คเกจใหม่ทั้งหมดให้เหมาะกับความต้องการของคุณ (ไม่ใช่ความคิดที่ดี) นี่เป็นวิธีเดียวที่ฉันคิดได้ สิ่งนี้ทำได้ในขณะที่ยังคงไม่มีตัวชี้MyStructอยู่ที่นั่น

นอกจากนี้คุณไม่จำเป็นต้องใช้โครงสร้างแบบอินไลน์คุณสามารถสร้างโครงสร้างที่ตั้งชื่อได้ ฉันใช้ LiteIDE กับการเติมโค้ดดังนั้นฉันชอบอินไลน์เพื่อหลีกเลี่ยงความยุ่งเหยิง


9

Dataเป็นโครงสร้างเริ่มต้นดังนั้นจึงไม่ถือว่าว่างเปล่าเพราะencoding/jsonดูเฉพาะค่าทันทีไม่ใช่ฟิลด์ภายในโครงสร้าง

น่าเสียดายที่กลับมาnilจากตอนjson.Marhslerนี้ใช้งานไม่ได้:

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

คุณสามารถให้Resultmarshaler ได้เช่นกัน แต่มันไม่คุ้มกับความพยายาม

ตัวเลือกเดียวที่เป็นแมตต์ชี้ให้เห็นคือการทำให้ตัวชี้และการตั้งค่าDatanil


1
ฉันไม่เห็นว่าเหตุใดจึงencoding/json ไม่สามารถตรวจสอบช่องย่อยของโครงสร้างได้ มันจะไม่มีประสิทธิภาพมากใช่ แต่ก็ไม่เป็นไปไม่ได้อย่างแน่นอน
nemo

@nemo ฉันเห็นจุดของคุณฉันเปลี่ยนคำพูด มันไม่ได้ทำเพราะมันจะไม่มีประสิทธิภาพ สามารถทำได้json.Marshalerเป็นกรณี ๆ ไป
Luke

2
มันเป็นไม่ได้ไปได้ที่จะตัดสินใจอากาศหรือไม่MyStructเป็นที่ว่างเปล่าโดยการใช้json.MarshalerในMyStructตัวของมันเอง พิสูจน์: play.golang.org/p/UEC8A3JGvx
ชาคริต

ในการทำเช่นนั้นคุณจะต้องติดตั้งjson.MarshalerในResultประเภทที่มีตัวเองซึ่งอาจไม่สะดวกมาก
chakrit

4

มีข้อเสนอ Golang ที่โดดเด่นสำหรับฟีเจอร์นี้ซึ่งเปิดใช้งานมานานกว่า 4 ปีแล้วดังนั้นในตอนนี้จึงสามารถสันนิษฐานได้อย่างปลอดภัยว่าจะไม่นำเข้าสู่ไลบรารีมาตรฐานในเร็ว ๆ นี้ ในฐานะที่เป็น @ Matt ชี้ให้เห็นการแบบดั้งเดิมวิธีการคือการแปลงstructsจะชี้ไป structs หากวิธีการนี้เป็นไปไม่ได้ (หรือทำไม่ได้) แล้วทางเลือกคือการใช้การเข้ารหัส JSON อื่นซึ่งไม่สนับสนุนถนัด structs

ฉันสร้างมิเรอร์ของไลบรารี Golang json ( clarketm / json ) โดยเพิ่มการสนับสนุนสำหรับการละเว้นโครงสร้างค่าศูนย์เมื่อใช้omitemptyแท็ก ไลบรารีนี้ตรวจจับความชัดเจนในลักษณะที่คล้ายคลึงกับgo-yamlตัวเข้ารหัส YAML ที่เป็นที่นิยมโดยการตรวจสอบฟิลด์โครงสร้างสาธารณะซ้ำ

เช่น

$ 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"
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.