Commit 731e1651 authored by Paul Warren's avatar Paul Warren
Browse files

Support for unmarshalling xml:lang attributes.

parent b8064cee
......@@ -87,6 +87,10 @@ import (
//
// * A struct field with tag "-" is never unmarshalled into.
//
// * If the struct has a field of type []byte or string with tag
// ",lang", Unmarshal records the value of the currently applicable
// xml:lang declaration.
//
// Unmarshal maps an XML element to a string or []byte by saving the
// concatenation of that element's character data in the string or
// []byte. The saved []byte is never nil.
......@@ -166,7 +170,7 @@ type Unmarshaler interface {
// UnmarshalXMLAttr is used only for struct fields with the
// "attr" option in the field tag.
type UnmarshalerAttr interface {
UnmarshalXMLAttr(attr Attr) error
UnmarshalXMLAttr(d *Decoder, attr Attr) error
}
// receiverType returns the receiver type to use in an expression like "%s.MethodName".
......@@ -236,12 +240,12 @@ func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error {
if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) {
// This is an unmarshaler with a non-pointer receiver,
// so it's likely to be incorrect, but we do what we're told.
return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(p, attr)
}
if val.CanAddr() {
pv := val.Addr()
if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) {
return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr)
return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(p, attr)
}
}
......@@ -431,6 +435,15 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
}
}
case fLang:
t := finfo.value(sv)
switch t.Kind() {
case reflect.String:
t.SetString(string(p.lang))
case reflect.Slice:
t.Set(reflect.ValueOf(p.lang))
}
case fCharData:
if !saveData.IsValid() {
saveData = finfo.value(sv)
......
......@@ -653,7 +653,7 @@ type MyAttr struct {
attr string
}
func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error {
func (m *MyAttr) UnmarshalXMLAttr(d *Decoder, attr Attr) error {
m.attr = attr.Value
return nil
}
......
......@@ -32,13 +32,14 @@ const (
fElement fieldFlags = 1 << iota
fAttr
fCharData
fLang
fInnerXml
fComment
fAny
fOmitEmpty
fMode = fElement | fAttr | fCharData | fInnerXml | fComment | fAny
fMode = fElement | fAttr | fCharData | fLang | fInnerXml | fComment | fAny
)
var tinfoMap = make(map[reflect.Type]*typeInfo)
......@@ -132,6 +133,8 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
finfo.flags |= fAttr
case "chardata":
finfo.flags |= fCharData
case "lang":
finfo.flags |= fLang
case "innerxml":
finfo.flags |= fInnerXml
case "comment":
......@@ -148,7 +151,7 @@ func structFieldInfo(typ reflect.Type, f *reflect.StructField) (*fieldInfo, erro
switch mode := finfo.flags & fMode; mode {
case 0:
finfo.flags |= fElement
case fAttr, fCharData, fInnerXml, fComment, fAny:
case fAttr, fCharData, fInnerXml, fComment, fAny, fLang:
if f.Name == "XMLName" || tag != "" && mode != fAttr {
valid = false
}
......
......@@ -4,6 +4,14 @@
// Package xml implements a simple XML 1.0 parser that
// understands XML name spaces.
// Blink Ace fork:
// This is a fork of encoding/xml that adds the following features:
// * Exposes NamespaceBindings on Decoder so that unmarshallers can resolve
// prefixes in order to support QName values
// * Add Decoder as a parameter to UnmarshallXMLAttr so that attribute
// unmarshallers can access NamespaceBindings() (to support QName values)
package xml
// References:
......@@ -194,6 +202,7 @@ type Decoder struct {
nextToken Token
nextByte int
ns map[string]string
lang string
err error
line int
offset int64
......@@ -266,6 +275,10 @@ func (d *Decoder) Token() (t Token, err error) {
d.pushNs(a.Name.Local, v, ok)
d.ns[a.Name.Local] = a.Value
}
if a.Name.Space == "xml" && a.Name.Local == "lang" {
d.pushLang(d.lang)
d.lang = a.Value
}
if a.Name.Space == "" && a.Name.Local == "xmlns" {
// Default space for untagged names
v, ok := d.ns[""]
......@@ -340,6 +353,7 @@ type stack struct {
const (
stkStart = iota
stkNs
stkLang
stkEOF
)
......@@ -379,7 +393,7 @@ func (d *Decoder) pushEOF() {
}
// The stkNs entries below a start are associated with that
// element too; skip over them.
for start.next != nil && start.next.kind == stkNs {
for start.next != nil && (start.next.kind == stkNs || start.next.kind == stkLang) {
start = start.next
}
s := d.free
......@@ -418,6 +432,13 @@ func (d *Decoder) pushNs(local string, url string, ok bool) {
s.ok = ok
}
// Record that we are changing the value of xml:lang.
// Store old language in s.name.Local
func (d *Decoder) pushLang(lang string) {
s := d.push(stkLang)
s.name.Local = lang
}
// Creates a SyntaxError with the current line number.
func (d *Decoder) syntaxError(msg string) error {
return &SyntaxError{Msg: msg, Line: d.line}
......@@ -455,10 +476,15 @@ func (d *Decoder) popElement(t *EndElement) bool {
// translations that were associated with the element we just closed.
for d.stk != nil && d.stk.kind != stkStart && d.stk.kind != stkEOF {
s := d.pop()
if s.ok {
d.ns[s.name.Local] = s.name.Space
} else {
delete(d.ns, s.name.Local)
switch {
case s.kind == stkNs:
if s.ok {
d.ns[s.name.Local] = s.name.Space
} else {
delete(d.ns, s.name.Local)
}
case s.kind == stkLang:
d.lang = s.name.Local
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment