Отменяющее сопоставление необязательное поле float64 возвращает ошибку при переходе

#xml #floating-point #go

#xml #значение с плавающей запятой #Вперед

Вопрос:

Внешний API, который использует мое приложение, иногда не возвращает значений для одного из полей float64. Когда это происходит, я не могу разархивировать остальную часть документа.

Вот пример кода в Go playground:

http://play.golang.org/p/Twv8b6KCtw

 package main

import (
    "encoding/xml"
    "fmt"
)

func main() {

    type Result struct {
        XMLName xml.Name `xml:"Person"`
        Grade   float64  `xml:"grade"`
        Name    string   `xml:"name"`
    }

    data := `
        <Person>
            <grade/>
            <name>Jack</name>
        </Person>
    `
    var v Result
    err := xml.Unmarshal([]byte(data), amp;v)
    if err != nil {
        fmt.Printf("error: %v", err)
        return
    }
    fmt.Printf("name: %sn", v.Name)

}
  

Если я изменю float64 в строке 12 на string, это сработает. Есть идеи, как я могу найти обходной путь для этого? Должен ли я просто определять каждое поле как строку и выполнять преобразование вручную?

Ответ №1:

Я полагаю, что вы обнаружили ошибку, вам следует сообщить об этом в системе отслеживания проблем, я отправил отчет об ошибке выпуска 8333.

Теперь для решения текущей проблемы у вас есть 2 варианта:

  1. Используйте string тип, который они ведут в вашем собственном диалоге, чтобы выводить по требованию.
  2. Полностью удалите <grade/> тег и используйте xml:"grade,omitempty" в своей структуре.

Комментарии:

1. Выполнение преобразования моими силами сводит на нет весь смысл определения всех полей в структурах. На самом деле я не могу использовать опцию omitempty, поскольку внешний API, который я использую, возвращает <grade/>. Возможно, мне следует изменить Unmarshal(), чтобы игнорировать создание подобных пустых элементов.

2. Ну, вы могли бы определить свою функцию диалога в структуре или просто запустить свой xml через strings.Replace(xml_string, "<grade/>", "", -1) но это ошибка.

3. Отлично. С нетерпением ждем его появления в следующем выпуске.

Ответ №2:

Чтобы добавить к решениям OneOfOne, вы также можете определить свой собственный UnmarshalXML метод.

Пример (При воспроизведении):

 type Grade float64

func (g *Grade) UnmarshalXML(d *xml.Decoder, s xml.StartElement) error {
    for tok, err := d.Token(); err != io.EOF; tok, err = d.Token() {
        if chrdata, ok := tok.(xml.CharData); ok {
            f, err := strconv.ParseFloat(string(chrdata), 64)
            if err != nil {
                return err
            }
            *(*float64)(g) = f
        }
    }
    return nil
}

type Result struct {
    XMLName xml.Name `xml:"Person"`
    Grade   Grade    `xml:"grade"`
    Name    string   `xml:"name"`
}
  

Это анализирует <grade> тег путем поиска CharData промежуточных токенов и их разбора как float, оставляя оценку на уровне 0.0 , когда нет значения для анализа.