如何以切片格式解析 XML
How to parse XML in slice format
我有这个XML:
<?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>
我不知道如何解析得到 [{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}]
并得到单独的值,例如 height
、weight
等。最后得到 641, 82, 97
和 1088
我根据找到的指南进行了尝试:
type DimensionInfo struct {
Answer struct {
FreeText []string `xml:"freetext"`
} `xml:"answer"`
}
var data = []byte(`<?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>`)
var t DimensionInfo
xml.Unmarshal(data, &t)
fmt.Println(t.Answer.FreeText)
我快空了slice/list
问题
有两个问题:
错误xml.Unmarshal
returns被忽略但函数在指定的输入数据流上失败,因为所谓的XML声明(即<?xml ... ?>
位) 声明数据流使用不同于 UTF-8 的编码进行编码——UTF-8 是标准指定的唯一有效编码——因此解码器只是拒绝继续。
接收解码数据位的struct
类型的字段用标签注释,提示XML解码器XML节点必须在数据流中找到的名称全是小写,这是不正确的。
由于解码器被明确告知要查找名为 "freetext" 的节点,因此它会忽略名为 "FreeText" 的节点,依此类推。
解决方案
XML 个节点和 struct
个标签的名称
检测感兴趣节点的问题很容易解决:只需删除 xml:
标签,解码器就能找到正确的节点。
另一种解决方案是利用解码器理解字段标签中 "nested" 名称的能力:您可以使用
删除额外的嵌套数据类型
type DimensionInfo struct {
FreeText []string `xml:"Answer>FreeText"`
}
这个标签告诉解码器用任何名为 FreeText
的节点的内容填充 FreeText
字段,该节点直接位于任何名为 Answer
.
的节点下
源数据的编码
编码的问题比较麻烦——一般情况下。
由于表示 XML 文档的唯一有效编码是 UTF-8(无 BOM),因此解码器希望其源数据采用 UTF-8 编码。
为了处理不同编码的数据流,解码器为程序员提供了一种方法来指定 "an adapter" 这将透明地将输入数据流重新编码为 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
,它是一个函数,如果指定,则调用它以获取 Decoder
时指定的源 io.Reader
] 和源数据流的字符集(编码),以及 returns 另一个 io.Reader
,当从中读取时,从源 reader 中提取数据,重新编码从源编码到 UTF-8 并将其交给 Decoder
.
在一般情况下,您可能会使用来自 golang.org/x/text/encoding
层次结构的几个包,它们能够理解大量过时的非 Unicode 和 Unicode 编码,并提供从它们到 UTF 的透明重新编码-8.
不过,在您的简单情况下,它可能是最简单的 1) 依赖于 ASCII 字符的编码方式与其各自的 Unicode 代码点使用 UTF-8 编码的方式完全相同; 2) 假设您的输入数据流是 really 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 and UTF-16 但 UTF-8 是网络上的实际标准。
我有这个XML:
<?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>
我不知道如何解析得到 [{"height":641,"label":"F-22 Raptor","left":82,"top":97,"width":1088}]
并得到单独的值,例如 height
、weight
等。最后得到 641, 82, 97
和 1088
我根据找到的指南进行了尝试:
type DimensionInfo struct {
Answer struct {
FreeText []string `xml:"freetext"`
} `xml:"answer"`
}
var data = []byte(`<?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>`)
var t DimensionInfo
xml.Unmarshal(data, &t)
fmt.Println(t.Answer.FreeText)
我快空了slice/list
问题
有两个问题:
错误
xml.Unmarshal
returns被忽略但函数在指定的输入数据流上失败,因为所谓的XML声明(即<?xml ... ?>
位) 声明数据流使用不同于 UTF-8 的编码进行编码——UTF-8 是标准指定的唯一有效编码——因此解码器只是拒绝继续。接收解码数据位的
struct
类型的字段用标签注释,提示XML解码器XML节点必须在数据流中找到的名称全是小写,这是不正确的。由于解码器被明确告知要查找名为 "freetext" 的节点,因此它会忽略名为 "FreeText" 的节点,依此类推。
解决方案
XML 个节点和 struct
个标签的名称
检测感兴趣节点的问题很容易解决:只需删除 xml:
标签,解码器就能找到正确的节点。
另一种解决方案是利用解码器理解字段标签中 "nested" 名称的能力:您可以使用
删除额外的嵌套数据类型type DimensionInfo struct {
FreeText []string `xml:"Answer>FreeText"`
}
这个标签告诉解码器用任何名为 FreeText
的节点的内容填充 FreeText
字段,该节点直接位于任何名为 Answer
.
源数据的编码
编码的问题比较麻烦——一般情况下。
由于表示 XML 文档的唯一有效编码是 UTF-8(无 BOM),因此解码器希望其源数据采用 UTF-8 编码。
为了处理不同编码的数据流,解码器为程序员提供了一种方法来指定 "an adapter" 这将透明地将输入数据流重新编码为 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
,它是一个函数,如果指定,则调用它以获取 Decoder
时指定的源 io.Reader
] 和源数据流的字符集(编码),以及 returns 另一个 io.Reader
,当从中读取时,从源 reader 中提取数据,重新编码从源编码到 UTF-8 并将其交给 Decoder
.
在一般情况下,您可能会使用来自 golang.org/x/text/encoding
层次结构的几个包,它们能够理解大量过时的非 Unicode 和 Unicode 编码,并提供从它们到 UTF 的透明重新编码-8.
不过,在您的简单情况下,它可能是最简单的 1) 依赖于 ASCII 字符的编码方式与其各自的 Unicode 代码点使用 UTF-8 编码的方式完全相同; 2) 假设您的输入数据流是 really 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 and UTF-16 但 UTF-8 是网络上的实际标准。