扩展golang xml解码器

时间:2019-05-31 11:00:06

标签: xml go struct

我有一个xml文件,可以通过使用源属性来包含相对于原始文件的其他xml文件。因此,UnmarshalXML方法应该有权访问原始xml文件的位置。我扩展了xml解码器并添加了目录字段。实现ExtendedUnmarshaler接口的结构可以访问目录。

以下代码显示了我尝试执行的操作。它仅在根标记包含源属性时才有效,因为在调用DecodeElement之后,整个文档将立即被解析,而我失去了对子标记的控制。

type ExtendedDecoder struct {
    *xml.Decoder
    cwd string  // current working directory
}

func Unmarshal(data []byte, v interface{}, cwd string) error {
    xmlDecoder := xml.NewDecoder(bytes.NewReader(data))
    return ExtendedDecoder{xmlDecoder, cwd}.DecodeElement(v, nil)
}

func (d *ExtendedDecoder) DecodeElement(v interface{}, start *xml.StartElement) error {
    v2, ok := v.(ExtendedUnmarshaler); if ok {
        return v2.ExtendedUnmarshalXML(d, *start)
    } else {
        // Here lies the problem. I need to parse the element with DecodeElement but by
        // doing so I lose control over all the sub elements.
        return d.Decoder.DecodeElement(v, start)
    }
}

type ExtendedUnmarshaler interface {
    ExtendedUnmarshalXML(d *ExtendedDecoder, start xml.StartElement) error
}

type Tag struct {
    Source string `xml:"source"`
}

func (t *Tag) ExtendedUnmarshalXML(d *ExtendedDecoder, start xml.StartElement) error {
    d.Decoder.DecodeElement(t, &start)
    if t.Source != "" {
        sourcePath := filepath.Join(d.cwd, t.Source)
        // Read file at sourcePath
    }
    return nil
}

func main() {
    path := "path/to/file.xml"
    data, _ := ioutil.ReadFile(path)

    t := Tag{}
    Unmarshal(data, t, filepath.Dir(path))
}

不处理错误,因为否则代码将更长。

除了根标签之外,有什么方法可以使此代码适用于其他标签吗?

我宁愿在遇到源文件时就对其进行解析,因为找到带有source属性的标签似乎并不是很未来的证明。每次添加带有源属性的新标签时,必须更新找到源属性的功能。

更新:查看源代码,这样可以解决问题;

func (d *ExtendedDecoder) DecodeElement(v interface{}, start *xml.StartElement) error {
    v2, ok := v.(ExtendedUnmarshaler); if ok {
        return v2.ExtendedUnmarshalXML(d, *start)
    } else {
        for {
            t, err := d.Decoder.Token()
            if err != nil {
                return err
            }
            switch se := t.(type) {
            case xml.StartElement:
                err := d.DecodeElement(saveAny, &se)
                if err != nil {
                    return err
                }
            case xml.EndElement:
                return d.Decoder.DecodeElement(v, start)
            }
        }
    }
}

此处“ saveAny”是与子标记相对应的结构。由于xml包中几乎所有有用的功能都没有导出,所以我不知道如何获取saveAny的值

0 个答案:

没有答案