Когда ваш XML-вход не кодируется в UTF-8, функция Unmarshal для пакета xml, по-видимому, требует CharsetReader.
Где вы находите такое?
Когда ваш XML-вход не кодируется в UTF-8, функция Unmarshal для пакета xml, по-видимому, требует CharsetReader.
Где вы находите такое?
Развернувшись на предложении @anschel-schaffer-cohen и комментарии @mjibson, используя go-charset, как указано выше, вы можете использовать эти три строки
decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReader
err = decoder.Decode(&parsed)
для достижения требуемого результата. просто не забудьте charset знать, где находятся файлы данных, вызывая
charset.CharsetDir = ".../src/code.google.com/p/go-charset/datafiles"
в тот момент, когда приложение запускается.
ИЗМЕНИТЬ
Вместо приведенного выше, charset.CharsetDir = и т.д. более разумно просто импортировать файлы данных. они рассматриваются как встроенный ресурс:
import (
"code.google.com/p/go-charset/charset"
_ "code.google.com/p/go-charset/data"
...
)
go install будет просто делать это, это также позволяет избежать головной боли развертывания (где/как я могу получить файлы данных относительно исполняемого приложения?).
используя импорт с подчеркиванием, просто вызывает пакет init() func, который загружает требуемый материал в память.
Обновленный ответ на 2015 год и далее:
import (
"encoding/xml"
"golang.org/x/net/html/charset"
)
decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReaderLabel
err = decoder.Decode(&parsed)
Здесь приведен пример программы Go, которая использует функцию CharsetReader для преобразования XML-ввода из ISO-8859-1 в UTF-8. Программа печатает XML-комментарии тестового файла.
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"utf8"
"xml"
)
type CharsetISO88591er struct {
r io.ByteReader
buf *bytes.Buffer
}
func NewCharsetISO88591(r io.Reader) *CharsetISO88591er {
buf := bytes.NewBuffer(make([]byte, 0, utf8.UTFMax))
return &CharsetISO88591er{r.(io.ByteReader), buf}
}
func (cs *CharsetISO88591er) ReadByte() (b byte, err os.Error) {
// http://unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
// Date: 1999 July 27; Last modified: 27-Feb-2001 05:08
if cs.buf.Len() <= 0 {
r, err := cs.r.ReadByte()
if err != nil {
return 0, err
}
if r < utf8.RuneSelf {
return r, nil
}
cs.buf.WriteRune(int(r))
}
return cs.buf.ReadByte()
}
func (cs *CharsetISO88591er) Read(p []byte) (int, os.Error) {
// Use ReadByte method.
return 0, os.EINVAL
}
func isCharset(charset string, names []string) bool {
charset = strings.ToLower(charset)
for _, n := range names {
if charset == strings.ToLower(n) {
return true
}
}
return false
}
func IsCharsetISO88591(charset string) bool {
// http://www.iana.org/assignments/character-sets
// (last updated 2010-11-04)
names := []string{
// Name
"ISO_8859-1:1987",
// Alias (preferred MIME name)
"ISO-8859-1",
// Aliases
"iso-ir-100",
"ISO_8859-1",
"latin1",
"l1",
"IBM819",
"CP819",
"csISOLatin1",
}
return isCharset(charset, names)
}
func IsCharsetUTF8(charset string) bool {
names := []string{
"UTF-8",
// Default
"",
}
return isCharset(charset, names)
}
func CharsetReader(charset string, input io.Reader) (io.Reader, os.Error) {
switch {
case IsCharsetUTF8(charset):
return input, nil
case IsCharsetISO88591(charset):
return NewCharsetISO88591(input), nil
}
return nil, os.NewError("CharsetReader: unexpected charset: " + charset)
}
func main() {
// Print the XML comments from the test file, which should
// contain most of the printable ISO-8859-1 characters.
r, err := os.Open("ISO88591.xml")
if err != nil {
fmt.Println(err)
return
}
defer r.Close()
fmt.Println("file:", r.Name())
p := xml.NewParser(r)
p.CharsetReader = CharsetReader
for t, err := p.Token(); t != nil && err == nil; t, err = p.Token() {
switch t := t.(type) {
case xml.ProcInst:
fmt.Println(t.Target, string(t.Inst))
case xml.Comment:
fmt.Println(string([]byte(t)))
}
}
}
Чтобы размонтировать XML с помощью encoding="ISO-8859-1" из io.Reader r в структуру result, используя функцию CharsetReader из программы для перевода с ISO-8859-1 на UTF-8, напишите:
p := xml.NewParser(r)
p.CharsetReader = CharsetReader
err := p.Unmarshal(&result, nil)
Кажется, есть внешняя библиотека, которая обрабатывает это: go-charset. Я сам не пробовал; это работает для вас?
Изменить: не используйте это, используйте ответ go-charset.
Здесь обновленная версия кода @peterSO, которая работает с go1:
package main
import (
"bytes"
"io"
"strings"
)
type CharsetISO88591er struct {
r io.ByteReader
buf *bytes.Buffer
}
func NewCharsetISO88591(r io.Reader) *CharsetISO88591er {
buf := bytes.Buffer{}
return &CharsetISO88591er{r.(io.ByteReader), &buf}
}
func (cs *CharsetISO88591er) Read(p []byte) (n int, err error) {
for _ = range p {
if r, err := cs.r.ReadByte(); err != nil {
break
} else {
cs.buf.WriteRune(rune(r))
}
}
return cs.buf.Read(p)
}
func isCharset(charset string, names []string) bool {
charset = strings.ToLower(charset)
for _, n := range names {
if charset == strings.ToLower(n) {
return true
}
}
return false
}
func IsCharsetISO88591(charset string) bool {
// http://www.iana.org/assignments/character-sets
// (last updated 2010-11-04)
names := []string{
// Name
"ISO_8859-1:1987",
// Alias (preferred MIME name)
"ISO-8859-1",
// Aliases
"iso-ir-100",
"ISO_8859-1",
"latin1",
"l1",
"IBM819",
"CP819",
"csISOLatin1",
}
return isCharset(charset, names)
}
func CharsetReader(charset string, input io.Reader) (io.Reader, error) {
if IsCharsetISO88591(charset) {
return NewCharsetISO88591(input), nil
}
return input, nil
}
Вызывается с помощью
d := xml.NewDecoder(reader)
d.CharsetReader = CharsetReader
err := d.Decode(&dst)
На данный момент в раздаточном дистрибутиве нет ни одного, ни другого, что я могу найти. Не удивительно, что этот крючок меньше месяца назад на момент написания.
Так как CharsetReader определяется как CharsetReader func(charset string, input io.Reader) (io.Reader, os.Error), вы можете сделать свой собственный.
Там один пример в тесты, но это может быть не совсем полезно для вас.