如何以切片格式解析 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}] 并得到单独的值,例如 heightweight 等。最后得到 641, 82, 971088

我根据找到的指南进行了尝试:

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

问题

有两个问题:

  1. 错误xml.Unmarshal returns被忽略但函数在指定的输入数据流上失败,因为所谓的XML声明(即<?xml ... ?> 位) 声明数据流使用不同于 UTF-8 的编码进行编码——UTF-8 是标准指定的唯一有效编码——因此解码器只是拒绝继续。

  2. 接收解码数据位的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 是网络上的实际标准。