亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

將平面 XML 解組為數據結構

將平面 XML 解組為數據結構

Go
慕無忌1623718 2022-11-28 17:07:55
我有一個平面 XML 結構,我試圖將其解組為一個 go 數據結構。我試圖找到一種方法從下面的 XML ie- bucket1 = [apple,orange,grapes], bucket2= [apple,mangoes] 中獲取每個桶中的項目列表(項目名稱)。當我嘗試將 xml 解組為下面的 go 數據結構時,我能夠獲得桶名和項目的列表,但我無法將項目列表映射到它們各自的桶,因為每個桶可以有很多項目。有沒有辦法通過更改go數據結構來從這個xml中實現這個需求?我無法控制 XML 的結構,因此無法更改它以滿足我的要求。我是新來的,我很感激這里的任何意見。type buckets struct {    XMLName    xml.Name `xml:"buckets"`    BucketName []string `xml:"bucket-name"`    ItemName   []string `xml:"item-name"`    Weight     []string `xml:"weight"`    Quantity   []string `xml:"quantity"`}                <?xml version="1.0" encoding="UTF-8"?>    <buckets>       <bucket-name>bucket1</bucket-name>       <item-name>apple</item-name>       <weight>500</weight>       <quantity>3</quantity>       <item-name>orange</item-name>       <weight>500</weight>       <quantity>2</quantity>       <item-name>grapes</item-name>       <weight>800</weight>       <quantity>1</quantity>       <bucket-name>bucket2</bucket-name>       <item-name>apple</item-name>       <weight>500</weight>       <quantity>3</quantity>       <item-name>mangoes</item-name>       <weight>400</weight>       <quantity>2</quantity>    </buckets>
查看完整描述

2 回答

?
有只小跳蛙

TA貢獻1824條經驗 獲得超8個贊

您可以通過使用自定義xml.UnmarshalXML并手動將存儲桶映射到 Go 結構來實現您正在嘗試做的事情。


下面描述的代碼假定 XML 元素的順序與所提供的示例相同。


首先,我們有問題中描述的結構:


type Buckets struct {

    XMLName xml.Name `xml:"buckets"`

    Buckets []*Bucket

}


type Bucket struct {

    BucketName string `xml:"Bucket-name"`

    Items      []*Item

}


type Item struct {

    Name     string `xml:"item-name"`

    Weight   int    `xml:"weight"`

    Quantity int    `xml:"quantity"`

}

接下來我們需要通過實現結構的方法來實現Unmarshaler接口。當我們調用并將結構作為目標傳遞時,將調用此方法。UnmarshalXMLBucketsxml.UnmarhsalBuckets


func (b *Buckets) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {

    b.XMLName = start.Name


    var currentBucket *Bucket

    var currentItem *Item

    for {

        t, err := d.Token()

        if t == nil {

            // append the last bucket before exiting

            b.Buckets = append(b.Buckets, currentBucket)

            break

        }

        if err != nil {

            return err

        }

        switch se := t.(type) {

        case xml.StartElement:

            switch se.Name.Local {

            case "Bucket-name":

                // check if currentBucket is nil, it is necessary for the first time that

                // is going to run. Otherwise, append the last bucket to the slice and reset it

                if currentBucket != nil {

                    b.Buckets = append(b.Buckets, currentBucket)

                }

                currentBucket = &Bucket{}


                if err := d.DecodeElement(&currentBucket.BucketName, &se); err != nil {

                    return err

                }

            case "item-name":

                currentItem = &Item{}

                if err := d.DecodeElement(&currentItem.Name, &se); err != nil {

                    return err

                }

            case "weight":

                if err := d.DecodeElement(&currentItem.Weight, &se); err != nil {

                    return err

                }

            case "quantity":

                if err := d.DecodeElement(&currentItem.Quantity, &se); err != nil {

                    return err

                }


                // since quantity comes last append the item to the bucket,  and reset it

                currentBucket.Items = append(currentBucket.Items, currentItem)

                currentItem = &Item{}

            }

        }

    }


    return nil

}

我們實質上所做的是遍歷 XML 元素并使用我們的自定義邏輯將它們映射到我們的結構。我不會詳細介紹d.Token()and xml.StartElement,您可以隨時閱讀文檔了解更多信息。

現在我們來分解一下上面的方法:

  • 當我們遇到一個名為 name 的元素時,Bucket-name我們知道后面有一個新的桶,所以將已經處理過的元素(我們必須檢查,nil因為第一次不會有任何處理)添加到切片并設置currentBucket為一個新的桶(我們要處理的那個)。

  • 當我們遇到一個帶有名稱的元素時,item-name我們知道后面有一個新項目,因此設置currentItem為一個新項目。

  • 當我們遇到一個名為 name 的元素時,quantity我們知道這是屬于 的最后一個元素currentItem,因此將它附加到currentBucket.Items

  • tfinally 變為 nil 時,它表示輸入流結束,但由于每當遇到新桶時我們都會追加一個桶,最后一個桶(或者如果只有一個桶)將不會被追加。所以,在我們break需要附加最后一個處理的之前。

筆記:

  • 您可以完全避免Buckets結構并創建一個函數來處理解組,方法是使用xml.Decoder類似的方法:

func UnmarshalBuckets(rawXML []byte) []*Bucket {

    // or any io.Reader that points to the xml data

    d := xml.NewDecoder(bytes.NewReader(rawXML))

    ...

}

免責聲明:

  • 我知道上面的代碼感覺有點粗略,我相信您可以改進它。隨意使用它并以更具可讀性的方式實現自定義邏輯。

  • 應該有一些我沒有涵蓋或在提供的示例中不存在的邊緣情況。您應該分析您的 XML 并嘗試(如果可能)覆蓋它們。

  • 如前所述,代碼在很大程度上依賴于 XML 元素的順序。

Go Playground的工作示例


查看完整回答
反對 回復 2022-11-28
?
蝴蝶刀刀

TA貢獻1801條經驗 獲得超8個贊

我同意 mkopriva。Go 的注釋針對用于相同結構的數據記錄的 XML 進行了優化。將它們用于混合內容就像給牛套上鞍座。plug:我已經在 GitHub 上編寫了處理混合內容的代碼,歡迎提供反饋。



查看完整回答
反對 回復 2022-11-28
  • 2 回答
  • 0 關注
  • 156 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號