Xml декодирование Golang
Тренируюсь с декодированием xml и возникла проблема. Почему-то при декодировке функция Unmarshal декодирует только первый элемент структуры. Все остальные - игнорируюстся, хотя прочитаны в переменной byteValue. Имеется XML -сттсруктура
<?xml version="1.0" encoding="UTF-8"?>
<Person id="13">
<name>
<first>Abigail</first>
<last>Moriarti</last>
</name>
</Person> <Person id="15">
<name>
<first>James</first>
<last>Willow</last>
</name>
</Person> <Person id="16">
<name>
<first>Margareth</first>
<last>O"Nil</last>
</name>
</Person> <Person id="17">
<name>
<first>Lowan</first>
<last>de Vega</last>
</name>
</Person>
Мой код для дешифровки:
type Person struct {
XMLName xml.Name `xml: "person"`
Id int `xml:"id,attr"`
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
}
func FullDecode() {
xmlFile, err := os.Open("EncodeData.xml")
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened EncodeData.xml")
defer xmlFile.Close()
byteValue, _ := ioutil.ReadAll(xmlFile)
var persons []Person
err = xml.Unmarshal(byteValue, &persons)
if err != nil {
log.Fatal("Cannot Decode xml")
}
Writefile, err := os.Create("DecodeData.txt")
for _, person := range persons {
finastring := string(person.Id) + " " + person.FirstName + " " + person.LastName
Writefile.WriteString(finastring)
}
}
Значение которое получается в результате:
1.
2. Abigail Moriarti
Почему в данном примере Unmarshal не декодирует весь файл, а останавливается на первом элементе?
Ответы (1 шт):
В XML документе на верхнем уровне должен быть ровно один элемент. Если таких элементов больше одного, то это некорректный XML документ.
Вам нужно обернуть список персон в какой-то тег. Например, <persons></persons>:
<?xml version="1.0" encoding="UTF-8"?>
<persons>
<person id="13">
<name>
<first>Abigail</first>
<last>Moriarti</last>
</name>
</person>
<person id="15">
<name>
<first>James</first>
<last>Willow</last>
</name>
</person>
<person id="16">
<name>
<first>Margareth</first>
<last>O"Nil</last>
</name>
</person>
<person id="17">
<name>
<first>Lowan</first>
<last>de Vega</last>
</name>
</person>
</persons>
Вот пример кода, который успешно парсит такой XML. Обратите внимание на Persons []Person в структуре Persons - именно этот массив указывает парсеру, что элементов person может быть более одного.
package main
import (
"encoding/xml"
"fmt"
"os"
)
func parseXMLFile[T any](fileName string, dst *T) error {
bytes, err := os.ReadFile(fileName)
if err != nil {
return err
}
return xml.Unmarshal(bytes, dst)
}
type Person struct {
XMLName xml.Name `xml:"person"`
Id int `xml:"id,attr"`
FirstName string `xml:"name>first"`
LastName string `xml:"name>last"`
}
type Persons struct {
XMLName xml.Name `xml:"persons"`
Persons []Person `xml:"person"`
}
func main() {
var persons Persons
if err := parseXMLFile("persons.xml", &persons); err != nil {
panic(err)
}
fmt.Printf("Persons: %v\n", persons.Persons)
}
UPDATE
если же вам нужно парсить некорректный XML файл, то это можно сделать при помощи потокового декодера xml.Decoder
Некорректный файл persons2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<person id="13">
<name>
<first>Abigail</first>
<last>Moriarti</last>
</name>
</person>
<person id="15">
<name>
<first>James</first>
<last>Willow</last>
</name>
</person>
<person id="16">
<name>
<first>Margareth</first>
<last>O"Nil</last>
</name>
</person>
<person id="17">
<name>
<first>Lowan</first>
<last>de Vega</last>
</name>
</person>
Как его парсить:
func main() {
bz, err := os.ReadFile("persons2.xml")
if err != nil {
panic(err)
}
r := bytes.NewReader(bz)
d := xml.NewDecoder(r)
var person Person
for d.Decode(&person) == nil {
fmt.Printf("Person: %v\n", person)
}
}
В цикле вызываете d.Decode до тех пор, пока вызов не вернёт ошибку.
Результат (https://go.dev/play/p/qfMb1kclPSi):
Person: {{ person} 13 Abigail Moriarti}
Person: {{ person} 15 James Willow}
Person: {{ person} 16 Margareth O"Nil}
Person: {{ person} 17 Lowan de Vega}