golang. Ошибка при анмаршалинге не прямого xml
При вписывании XML-данных напрямую в переменную, как в коде ниже все выводится как и должно.
package main
import (
"encoding/xml"
"fmt"
"net/http"
"io"
"log"
)
type ValCurs struct {
XMLName xml.Name `xml:"ValCurs"`
Valute []struct {
NumCode string `xml:"NumCode"`
CharCode string `xml:"CharCode"`
Nominal string `xml:"Nominal"`
Name string `xml:"Name"`
Value string `xml:"Value"`
VunitRate string `xml:"VunitRate"`
} `xml:"Valute"`
}
func main() {
data := `
<ValCurs Date="26.10.2024" name="Foreign Currency Market">
<Valute ID="R01010">
<NumCode>036</NumCode>
<CharCode>AUD</CharCode>
<Nominal>1</Nominal>
<Name>Австралийский доллар</Name>
<Value>64,0024</Value>
<VunitRate>64,0024</VunitRate>
</Valute>
</ValCurs>
`
valCurs := new(ValCurs)
err := xml.Unmarshal([]byte(data), valCurs)
if err != nil {
log.Fatal(err)
}
for _, valute := range valCurs.Valute {
fmt.Printf("%s: 1 %s = %v\n", valute.CharCode, valute.Name, valute.Value)
}
}
Но при попытке получить их через get-запрос выводится ошибка
XML syntax error on line 1: unquoted or missing attribute value in element
Никак не могу понять как ее исправить. Вот код выдающий ошибку
package main
import (
"encoding/xml"
"fmt"
"io"
"log"
"net/http"
)
type ValCurs struct {
XMLName xml.Name `xml:"ValCurs"`
Valute []struct {
NumCode string `xml:"NumCode"`
CharCode string `xml:"CharCode"`
Nominal string `xml:"Nominal"`
Name string `xml:"Name"`
Value string `xml:"Value"`
VunitRate string `xml:"VunitRate"`
} `xml:"Valute"`
}
func main() {
url := "https://cbr.ru/scripts/XML_daily.asp?date_req=27/10/2024"
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
data, err := io.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
valCurs := new(ValCurs)
err = xml.Unmarshal([]byte(data), valCurs)
if err != nil {
log.Fatal(err)
}
for _, valute := range valCurs.Valute {
fmt.Printf("%s: 1 %s = %v\n", valute.CharCode, valute.Name, valute.Value)
}
}
Ответы (1 шт):
В вашем вопросе сразу несколько проблем.
Во-первых, сайт Центробанка возвращает не XML документ, а HTML страницу с ошибкой 403. Нужно правильным образом подобрать заголовки, чтобы Центробанк вернул именно то что вам нужно
Во-вторых, документ возвращается в кодировке Windows-1251. Вам нужно настроить XML parser на разбор документов в не-юникодной кодировке
Вот мой вариант. Это приложеньице принимает флаг -file FILENAME
чтобы парсить документ из файла.
По-умолчанию парсится документ из интернета.
Для того, чтобы Центробанк отдал документ, приложение притворяется curl-ом.
package main
import (
"bytes"
"encoding/xml"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"golang.org/x/net/html/charset"
)
var (
fileFlag = flag.String("file", "", "Parse from file")
)
type ValCurs struct {
XMLName xml.Name `xml:"ValCurs"`
Valute []struct {
NumCode string `xml:"NumCode"`
CharCode string `xml:"CharCode"`
Nominal string `xml:"Nominal"`
Name string `xml:"Name"`
Value string `xml:"Value"`
VunitRate string `xml:"VunitRate"`
} `xml:"Valute"`
}
func main() {
flag.Parse()
data := bytes.Buffer{}
if *fileFlag != "" {
file, err := os.Open(*fileFlag)
if err != nil {
panic(err)
}
io.Copy(&data, file)
} else {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://cbr.ru/scripts/XML_daily.asp?date_req=27/10/2024", nil)
if err != nil {
log.Fatalln(err)
}
req.Header.Set("User-Agent", "curl/7.54.1")
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
io.Copy(&data, res.Body)
}
// fmt.Println(data.String())
valCurs := new(ValCurs)
// Handle windows-1251
decoder := xml.NewDecoder(&data)
decoder.CharsetReader = charset.NewReaderLabel
err := decoder.Decode(valCurs)
if err != nil {
log.Fatal(err)
}
for _, valute := range valCurs.Valute {
fmt.Printf("%s: 1 %s = %v\n", valute.CharCode, valute.Name, valute.Value)
}
}