Golang解析XML时如何处理缺失的属性或标签

Golang解析XML时如何处理缺失的属性或标签 大家好,

我刚开始学习Go语言,我的第一个迷你项目是尝试编写一个非常简单的代码片段,用于将XML转换为CSV。

我能够相当轻松地让代码片段运行起来,但在处理一个简单的标签缺失边界情况时遇到了困难。在下面Playground链接中的示例中;当XML的第二部分缺少LastName时,我希望代码片段用空字符串更新切片。目前的情况是,当标签缺失时,代码片段会完全忽略该元素,这会在写入CSV时导致问题。

https://play.golang.org/p/JX10U9hH1c3

当前LastName输出: [Raboy Doe]

期望的LastName输出: [Raboy “” Doe]

谢谢。


更多关于Golang解析XML时如何处理缺失的属性或标签的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

谢谢肖恩——这个方法奏效了。

更多关于Golang解析XML时如何处理缺失的属性或标签的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


更改您正在解组的目标模型:https://play.golang.org/p/16-J0L-eN-a

在Go中处理XML时,当标签缺失时确实需要特殊处理。你可以通过检查xml.Unmarshal的解析结果,或者在结构体中使用指针类型来区分"缺失"和"空值"。

以下是修改后的代码示例:

package main

import (
    "encoding/xml"
    "fmt"
)

type Person struct {
    XMLName  xml.Name `xml:"Person"`
    First    string   `xml:"FirstName"`
    Last     *string  `xml:"LastName"` // 使用指针类型
}

type People struct {
    XMLName xml.Name `xml:"People"`
    Person  []Person `xml:"Person"`
}

func main() {
    xmlData := `
<People>
    <Person>
        <FirstName>Michael</FirstName>
        <LastName>Raboy</LastName>
    </Person>
    <Person>
        <FirstName>John</FirstName>
    </Person>
    <Person>
        <FirstName>Jane</FirstName>
        <LastName>Doe</LastName>
    </Person>
</People>`

    var people People
    err := xml.Unmarshal([]byte(xmlData), &people)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    var lastNames []string
    for _, person := range people.Person {
        if person.Last != nil {
            lastNames = append(lastNames, *person.Last)
        } else {
            lastNames = append(lastNames, "") // 缺失时用空字符串
        }
    }

    fmt.Println("LastNames:", lastNames)
}

另一种方法是使用自定义的UnmarshalXML方法:

package main

import (
    "encoding/xml"
    "fmt"
    "strings"
)

type Person struct {
    XMLName  xml.Name `xml:"Person"`
    First    string   `xml:"FirstName"`
    Last     string   `xml:"LastName"`
}

func (p *Person) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    type Alias Person
    temp := &struct {
        Last *string `xml:"LastName"`
        *Alias
    }{
        Alias: (*Alias)(p),
    }
    
    if err := d.DecodeElement(temp, &start); err != nil {
        return err
    }
    
    if temp.Last != nil {
        p.Last = *temp.Last
    } else {
        p.Last = "" // 显式设置为空字符串
    }
    
    return nil
}

type People struct {
    XMLName xml.Name `xml:"People"`
    Person  []Person `xml:"Person"`
}

func main() {
    xmlData := `
<People>
    <Person>
        <FirstName>Michael</FirstName>
        <LastName>Raboy</LastName>
    </Person>
    <Person>
        <FirstName>John</FirstName>
    </Person>
    <Person>
        <FirstName>Jane</FirstName>
        <LastName>Doe</LastName>
    </Person>
</People>`

    var people People
    err := xml.Unmarshal([]byte(xmlData), &people)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    var lastNames []string
    for _, person := range people.Person {
        lastNames = append(lastNames, person.Last)
    }

    fmt.Println("LastNames:", lastNames)
}

如果你需要更细粒度的控制,可以直接使用xml.Decoder

package main

import (
    "encoding/xml"
    "fmt"
    "io"
    "strings"
)

func main() {
    xmlData := `
<People>
    <Person>
        <FirstName>Michael</FirstName>
        <LastName>Raboy</LastName>
    </Person>
    <Person>
        <FirstName>John</FirstName>
    </Person>
    <Person>
        <FirstName>Jane</FirstName>
        <LastName>Doe</LastName>
    </Person>
</People>`

    decoder := xml.NewDecoder(strings.NewReader(xmlData))
    var lastNames []string
    var inLastName bool
    var lastName string
    
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            fmt.Println("Error:", err)
            return
        }

        switch se := token.(type) {
        case xml.StartElement:
            if se.Name.Local == "LastName" {
                inLastName = true
            } else if se.Name.Local == "Person" {
                lastName = "" // 重置为默认值
            }
        case xml.EndElement:
            if se.Name.Local == "Person" {
                lastNames = append(lastNames, lastName)
            }
        case xml.CharData:
            if inLastName {
                lastName = string(se)
                inLastName = false
            }
        }
    }

    fmt.Println("LastNames:", lastNames)
}

第一个使用指针的解决方案是最简洁的,它能明确区分标签缺失(nil指针)和空值(指向空字符串的指针)。

回到顶部