Проблемы
Есть две проблемы:
Ошибка, возвращаемая xml.Unmarshal
, игнорируется, но происходит сбой функции в указанном потоке входных данных, так как вызываемое XML объявление (которое <?xml ... ?>
бит) объявляет, что поток данных кодируется с использованием кодировки, отличной от UTF-8 - единственной допустимой кодировки, определенной стандартом¹, - и поэтому декодер просто отказывается продолжать.
Поля типа struct
для приема декодированных битов данных снабжены метками, указывающими декодеру XML, что все узлы XML, которые он должен найти в потоке данных, имеют все имена в нижнем регистре, которые не соответствуют действительности.
Поскольку декодеру было явно сказано, скажем, искать узел с именем "свободный текст", он игнорирует узлы с именем "FreeText" и т. д.
Решения
Имена XML узлов и struct
тегов
Задача обнаружения интересующих узлов - это простое решение: просто удалите xml:
тегов и декодер сможет найти правильные узлы.
Альтернативное решение заключается в использовании способности декодера понимать «вложенные» имена в тегах полей: вы можете удалить дополнительный вложенный тип данных, используя
type DimensionInfo struct {
FreeText []string `xml:"Answer>FreeText"`
}
Этот тег указывает декодеру заполнить поле FreeText
содержимым любого узла с именем FreeText
, который расположен непосредственно под любым узлом с именем Answer
.
Кодировка источника data
Проблема с кодированием является более громоздкой - в общем случае.
Поскольку единственное допустимое кодирование для представления XML документов - это UTF-8 (без BOM), декодер ожидает, что его исходные данные должны быть в кодировке UTF-8.
Для обработки потоков данных, закодированных по-разному, декодер предоставляет программисту способ указать «адаптер», который бы прозрачно перекодировал поток входных данных в UTF. -8-кодированный поток данных, который затем будет использовать декодер.
Чтобы сделать это, вы должны создать (и настроить) экземпляр xml.Decoder
вместо прямого запуска xml.Unmarshal
(что само по себе создает одноразовый Decoder
).
В вашем случае, обращение к xml.Unmarshal(data, &t)
в использование явно созданного xml.Decoder
- это просто:
dec := xml.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&t); err != nil {
// handle the error
}
// handle the result
Поле интереса Decoder
: CharsetReader
, которое представляет собой функцию, которая, если указана, вызывается для получения указанного источника io.Reader
когда был создан Decoder
и набор символов (кодировка) потока исходных данных, и возвращает другой io.Reader
, который при чтении из извлекает данные из считывателя источника, перекодирует их из кодировки источника в UTF -8 и передает его Decoder
.
В общем случае вы, вероятно, будете использовать несколько пакетов из иерархии golang.org/x/text/encoding
, которые способны понимать множество устаревшие не-Unicode и Unicode-кодировки и обеспечивают прозрачное перекодирование из них в UTF-8.
Тем не менее, в вашем простом случае это, вероятно, самый простой способ 1) полагаться на тот факт, что символы ASCII кодируются точно y так же, как их соответствующие кодовые точки Unicode кодируются с использованием UTF-8; 2) предположим, что ваш поток входных данных действительно ASCII (и не содержит странных символов с кодовыми точками в диапазоне [128..255]. В этом случае мы можем избежать использования источника io.Reader
без перекодирования, что приводит нас к следующему решению:
var dec = xml.NewDecoder(bytes.NewReader(data))
dec.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
switch strings.ToLower(charset) {
case "ascii", "utf-8":
return input, nil
default:
return nil, fmt.Errorf("cannot handle XML encoding: %s", charset)
}
}
Пример решения
package main
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"log"
"strings"
)
const data = `<?xml version="1.0" encoding="ASCII"?>
<QuestionFormAnswers xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2005-10-01/QuestionFormAnswers.xsd">
<Answer>
<QuestionIdentifier>annotatedResult.boundingBoxes</QuestionIdentifier>
<FreeText>[{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}]</FreeText>
</Answer>
<Answer>
<QuestionIdentifier>annotatedResult.inputImageProperties.height</QuestionIdentifier>
<FreeText>839</FreeText>
</Answer>
<Answer>
<QuestionIdentifier>annotatedResult.inputImageProperties.width</QuestionIdentifier>
<FreeText>1260</FreeText>
</Answer>
</QuestionFormAnswers>`
func main() {
type DimensionInfo struct {
FreeText []string `xml:"Answer>FreeText"`
}
var dec = xml.NewDecoder(strings.NewReader(data))
dec.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
switch strings.ToLower(charset) {
case "ascii", "utf-8":
return input, nil
default:
return nil, fmt.Errorf("cannot handle XML encoding: %s", charset)
}
}
var t DimensionInfo
err := dec.Decode(&t)
if err != nil {
log.Fatal(err)
}
fmt.Println(t.FreeText)
}
¹ Строго говоря, это не так: XML процессоры должен понимать UTF-8 и UTF-16 , но UTF-8 является стандартом де-факто для 'net.