猿问

将平面 XML 解组为数据结构

我有一个平面 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>


慕无忌1623718
浏览 181回答 2
2回答

有只小跳蛙

您可以通过使用自定义xml.UnmarshalXML并手动将存储桶映射到 Go 结构来实现您正在尝试做的事情。下面描述的代码假定 XML 元素的顺序与所提供的示例相同。首先,我们有问题中描述的结构:type Buckets struct {&nbsp; &nbsp; XMLName xml.Name `xml:"buckets"`&nbsp; &nbsp; Buckets []*Bucket}type Bucket struct {&nbsp; &nbsp; BucketName string `xml:"Bucket-name"`&nbsp; &nbsp; Items&nbsp; &nbsp; &nbsp; []*Item}type Item struct {&nbsp; &nbsp; Name&nbsp; &nbsp; &nbsp;string `xml:"item-name"`&nbsp; &nbsp; Weight&nbsp; &nbsp;int&nbsp; &nbsp; `xml:"weight"`&nbsp; &nbsp; Quantity int&nbsp; &nbsp; `xml:"quantity"`}接下来我们需要通过实现结构的方法来实现Unmarshaler接口。当我们调用并将结构作为目标传递时,将调用此方法。UnmarshalXMLBucketsxml.UnmarhsalBucketsfunc (b *Buckets) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {&nbsp; &nbsp; b.XMLName = start.Name&nbsp; &nbsp; var currentBucket *Bucket&nbsp; &nbsp; var currentItem *Item&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; t, err := d.Token()&nbsp; &nbsp; &nbsp; &nbsp; if t == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // append the last bucket before exiting&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; b.Buckets = append(b.Buckets, currentBucket)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; switch se := t.(type) {&nbsp; &nbsp; &nbsp; &nbsp; case xml.StartElement:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; switch se.Name.Local {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "Bucket-name":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // check if currentBucket is nil, it is necessary for the first time that&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // is going to run. Otherwise, append the last bucket to the slice and reset it&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if currentBucket != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; b.Buckets = append(b.Buckets, currentBucket)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currentBucket = &Bucket{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err := d.DecodeElement(&currentBucket.BucketName, &se); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "item-name":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currentItem = &Item{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err := d.DecodeElement(&currentItem.Name, &se); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "weight":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err := d.DecodeElement(&currentItem.Weight, &se); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case "quantity":&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err := d.DecodeElement(&currentItem.Quantity, &se); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // since quantity comes last append the item to the bucket,&nbsp; and reset it&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currentBucket.Items = append(currentBucket.Items, currentItem)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; currentItem = &Item{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; 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 {&nbsp; &nbsp; // or any io.Reader that points to the xml data&nbsp; &nbsp; d := xml.NewDecoder(bytes.NewReader(rawXML))&nbsp; &nbsp; ...}免责声明:我知道上面的代码感觉有点粗略,我相信您可以改进它。随意使用它并以更具可读性的方式实现自定义逻辑。应该有一些我没有涵盖或在提供的示例中不存在的边缘情况。您应该分析您的 XML 并尝试(如果可能)覆盖它们。如前所述,代码在很大程度上依赖于 XML 元素的顺序。Go Playground的工作示例

蝴蝶刀刀

我同意 mkopriva。Go 的注释针对用于相同结构的数据记录的 XML 进行了优化。将它们用于混合内容就像给牛套上鞍座。plug:我已经在 GitHub 上编写了处理混合内容的代码,欢迎提供反馈。
随时随地看视频慕课网APP

相关分类

Go
我要回答