我有一个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的值