Golang如何将动态XML数据解析到结构体

Golang如何将动态XML数据解析到结构体 你好,

我有一个XML数据,其中包含一些具有动态元素名称的重复数据,如下所示。

<Ht>
	<Criteria>
		<ADNode_1>2</ADNode_1>
		<CDNode_1>2</CDNode_1>
		<IFNode_1>0</IFNode_1>
		<ADNode_2>2</ADNode_2>
		<CDNode_2>0</CDNode_2>
		<IFNode_2>0</IFNode_2>
		<ADNode_3>0</ADNode_3>
		<CDNode_3>0</CDNode_3>
		<IFNode_3>0</IFNode_3>
	</Criteria>
	<session id="1056134770841202228344907">
		<Htd ID="21170">
			<Data_1>
				<Info Count="2"></Info>
				<Data Id="iV29308/B2/R1">
					<Type>TR1</Type>
				</Data>
				<Data Id="iV29308/B3/R1">
					<Type>TR1</Type>
				</Data>
				<Data Id="iV29308/B4/R1">
					<Type>TR1</Type>
				</Data>
				<Data Id="iV29308/B6/R1">
					<Type>TR1</Type>
				</Data>
			</Data_1>
			<Data_2>
				<Info Count="2"></Info>
				<Data Id="iV29308/B2/R1">
					<Type>TR2</Type>
				</Data>
				<Data Id="iV29308/B3/R1">
					<Type>TR2</Type>
				</Data>
				<Data Id="iV29308/B4/R1">
					<Type>TR2</Type>
				</Data>
				<Data Id="iV29308/B6/R1">
					<Type>TR3</Type>
				</Data>
			</Data_2>
		</Htd>
	</session>
</Ht>

我可以为 <ADNode_1><ADNode_2><ADNode_3><Data_1><Data_2> 创建单独的结构体,但这些节点的数量可以是 n 个。 如下所示

<ADNode_1>2</ADNode_1>
<ADNode_2>2</ADNode_2>
<ADNode_3>2</ADNode_3>
.
.
<ADNode_n>2</ADNode_n>

或者

<Data_1></Data_1>
<Data_2></Data_2>
<Data_3></Data_3>
.
.
.
<Data_n></Data_n>

我应该如何创建这些节点的结构体,以容纳 n 个节点或元素?

这是 playground 链接。


更多关于Golang如何将动态XML数据解析到结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang如何将动态XML数据解析到结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


对于动态XML元素名称,可以使用xml.Unmarshal配合自定义的UnmarshalXML方法。以下是处理动态节点名称的示例:

package main

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

type Ht struct {
    XMLName  xml.Name `xml:"Ht"`
    Criteria Criteria  `xml:"Criteria"`
    Session  Session   `xml:"session"`
}

type Criteria struct {
    XMLName xml.Name `xml:"Criteria"`
    Nodes   []Node   `xml:",any"`
}

type Node struct {
    XMLName xml.Name
    Value   int `xml:",chardata"`
}

func (n *Node) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    n.XMLName = start.Name
    var content string
    if err := d.DecodeElement(&content, &start); err != nil {
        return err
    }
    val, err := strconv.Atoi(content)
    if err != nil {
        return err
    }
    n.Value = val
    return nil
}

type Session struct {
    XMLName xml.Name `xml:"session"`
    ID      string   `xml:"id,attr"`
    Htd     Htd      `xml:"Htd"`
}

type Htd struct {
    XMLName xml.Name `xml:"Htd"`
    ID      string   `xml:"ID,attr"`
    Data    []Data   `xml:",any"`
}

type Data struct {
    XMLName xml.Name `xml:",any"`
    Info    Info     `xml:"Info"`
    Items   []Item   `xml:"Data"`
}

type Info struct {
    XMLName xml.Name `xml:"Info"`
    Count   int      `xml:"Count,attr"`
}

type Item struct {
    XMLName xml.Name `xml:"Data"`
    ID      string   `xml:"Id,attr"`
    Type    string   `xml:"Type"`
}

func main() {
    xmlData := `<Ht>
    <Criteria>
        <ADNode_1>2</ADNode_1>
        <CDNode_1>2</CDNode_1>
        <IFNode_1>0</IFNode_1>
        <ADNode_2>2</ADNode_2>
        <CDNode_2>0</CDNode_2>
        <IFNode_2>0</IFNode_2>
        <ADNode_3>0</ADNode_3>
        <CDNode_3>0</CDNode_3>
        <IFNode_3>0</IFNode_3>
    </Criteria>
    <session id="1056134770841202228344907">
        <Htd ID="21170">
            <Data_1>
                <Info Count="2"></Info>
                <Data Id="iV29308/B2/R1">
                    <Type>TR1</Type>
                </Data>
                <Data Id="iV29308/B3/R1">
                    <Type>TR1</Type>
                </Data>
                <Data Id="iV29308/B4/R1">
                    <Type>TR1</Type>
                </Data>
                <Data Id="iV29308/B6/R1">
                    <Type>TR1</Type>
                </Data>
            </Data_1>
            <Data_2>
                <Info Count="2"></Info>
                <Data Id="iV29308/B2/R1">
                    <Type>TR2</Type>
                </Data>
                <Data Id="iV29308/B3/R1">
                    <Type>TR2</Type>
                </Data>
                <Data Id="iV29308/B4/R1">
                    <Type>TR2</Type>
                </Data>
                <Data Id="iV29308/B6/R1">
                    <Type>TR3</Type>
                </Data>
            </Data_2>
        </Htd>
    </session>
</Ht>`

    var ht Ht
    err := xml.Unmarshal([]byte(xmlData), &ht)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Criteria节点数量: %d\n", len(ht.Criteria.Nodes))
    for _, node := range ht.Criteria.Nodes {
        fmt.Printf("节点名: %s, 值: %d\n", node.XMLName.Local, node.Value)
    }

    fmt.Printf("\nData节点数量: %d\n", len(ht.Session.Htd.Data))
    for _, data := range ht.Session.Htd.Data {
        fmt.Printf("Data节点名: %s, Info.Count: %d\n", data.XMLName.Local, data.Info.Count)
        for _, item := range data.Items {
            fmt.Printf("  Item ID: %s, Type: %s\n", item.ID, item.Type)
        }
    }
}

对于更复杂的动态XML处理,可以使用xml.Decoder进行流式解析:

package main

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

func parseDynamicXML(xmlStr string) {
    decoder := xml.NewDecoder(strings.NewReader(xmlStr))
    
    for {
        token, err := decoder.Token()
        if err == io.EOF {
            break
        }
        if err != nil {
            panic(err)
        }

        switch se := token.(type) {
        case xml.StartElement:
            if se.Name.Local == "Criteria" {
                parseCriteria(decoder)
            } else if se.Name.Local == "Htd" {
                parseHtd(decoder)
            }
        }
    }
}

func parseCriteria(decoder *xml.Decoder) {
    for {
        token, err := decoder.Token()
        if err != nil {
            break
        }

        switch se := token.(type) {
        case xml.StartElement:
            if strings.HasPrefix(se.Name.Local, "ADNode_") ||
               strings.HasPrefix(se.Name.Local, "CDNode_") ||
               strings.HasPrefix(se.Name.Local, "IFNode_") {
                var value string
                decoder.DecodeElement(&value, &se)
                fmt.Printf("动态节点: %s = %s\n", se.Name.Local, value)
            }
        case xml.EndElement:
            if se.Name.Local == "Criteria" {
                return
            }
        }
    }
}

func parseHtd(decoder *xml.Decoder) {
    for {
        token, err := decoder.Token()
        if err != nil {
            break
        }

        switch se := token.(type) {
        case xml.StartElement:
            if strings.HasPrefix(se.Name.Local, "Data_") {
                parseDataSection(decoder, se.Name.Local)
            }
        case xml.EndElement:
            if se.Name.Local == "Htd" {
                return
            }
        }
    }
}

func parseDataSection(decoder *xml.Decoder, sectionName string) {
    fmt.Printf("处理数据段: %s\n", sectionName)
    
    for {
        token, err := decoder.Token()
        if err != nil {
            break
        }

        switch se := token.(type) {
        case xml.StartElement:
            if se.Name.Local == "Info" {
                var info struct {
                    Count int `xml:"Count,attr"`
                }
                decoder.DecodeElement(&info, &se)
                fmt.Printf("  Info.Count: %d\n", info.Count)
            } else if se.Name.Local == "Data" {
                var data struct {
                    ID   string `xml:"Id,attr"`
                    Type string `xml:"Type"`
                }
                decoder.DecodeElement(&data, &se)
                fmt.Printf("  Data: ID=%s, Type=%s\n", data.ID, data.Type)
            }
        case xml.EndElement:
            if se.Name.Local == sectionName {
                return
            }
        }
    }
}

func main() {
    xmlData := `<Ht>
    <Criteria>
        <ADNode_1>2</ADNode_1>
        <CDNode_1>2</CDNode_1>
        <IFNode_1>0</IFNode_1>
    </Criteria>
    <session id="1056134770841202228344907">
        <Htd ID="21170">
            <Data_1>
                <Info Count="2"></Info>
                <Data Id="iV29308/B2/R1">
                    <Type>TR1</Type>
                </Data>
            </Data_1>
        </Htd>
    </session>
</Ht>`

    parseDynamicXML(xmlData)
}

使用xml:",any"标签配合自定义的UnmarshalXML方法可以处理动态元素名称,而流式解析则提供了更大的灵活性来处理复杂的XML结构。

回到顶部