Commit bdf8bf6a authored by Russ Cox's avatar Russ Cox

encoding/xml: predefine xml name space prefix

Also change prefix generation to use more human-friendly prefixes.

Fixes #5040.

R=golang-dev, r, bradfitz
CC=golang-dev
https://golang.org/cl/7777047
parent 8c2b6226
...@@ -126,6 +126,70 @@ type printer struct { ...@@ -126,6 +126,70 @@ type printer struct {
depth int depth int
indentedIn bool indentedIn bool
putNewline bool putNewline bool
attrNS map[string]string // map prefix -> name space
attrPrefix map[string]string // map name space -> prefix
}
// createAttrPrefix finds the name space prefix attribute to use for the given name space,
// defining a new prefix if necessary. It returns the prefix and whether it is new.
func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
if prefix = p.attrPrefix[url]; prefix != "" {
return prefix, false
}
// The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
// and must be referred to that way.
// (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
// but users should not be trying to use that one directly - that's our job.)
if url == xmlURL {
return "xml", false
}
// Need to define a new name space.
if p.attrPrefix == nil {
p.attrPrefix = make(map[string]string)
p.attrNS = make(map[string]string)
}
// Pick a name. We try to use the final element of the path
// but fall back to _.
prefix = strings.TrimRight(url, "/")
if i := strings.LastIndex(prefix, "/"); i >= 0 {
prefix = prefix[i+1:]
}
if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") {
prefix = "_"
}
if strings.HasPrefix(prefix, "xml") {
// xmlanything is reserved.
prefix = "_" + prefix
}
if p.attrNS[prefix] != "" {
// Name is taken. Find a better one.
for p.seq++; ; p.seq++ {
if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" {
prefix = id
break
}
}
}
p.attrPrefix[url] = prefix
p.attrNS[prefix] = url
p.WriteString(`xmlns:`)
p.WriteString(prefix)
p.WriteString(`="`)
EscapeText(p, []byte(url))
p.WriteString(`" `)
return prefix, true
}
// deleteAttrPrefix removes an attribute name space prefix.
func (p *printer) deleteAttrPrefix(prefix string) {
delete(p.attrPrefix, p.attrNS[prefix])
delete(p.attrNS, prefix)
} }
// marshalValue writes one or more XML elements representing val. // marshalValue writes one or more XML elements representing val.
...@@ -212,17 +276,11 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { ...@@ -212,17 +276,11 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
} }
p.WriteByte(' ') p.WriteByte(' ')
if finfo.xmlns != "" { if finfo.xmlns != "" {
p.WriteString("xmlns:") prefix, created := p.createAttrPrefix(finfo.xmlns)
p.seq++ if created {
id := "_" + strconv.Itoa(p.seq) defer p.deleteAttrPrefix(prefix)
p.WriteString(id)
p.WriteString(`="`)
// TODO: EscapeString, to avoid the allocation.
if err := EscapeText(p, []byte(finfo.xmlns)); err != nil {
return err
} }
p.WriteString(`" `) p.WriteString(prefix)
p.WriteString(id)
p.WriteByte(':') p.WriteByte(':')
} }
p.WriteString(finfo.name) p.WriteString(finfo.name)
......
...@@ -503,6 +503,11 @@ type TableAttrs struct { ...@@ -503,6 +503,11 @@ type TableAttrs struct {
type TAttr struct { type TAttr struct {
HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"` HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"`
FTable string `xml:"http://www.w3schools.com/furniture table,attr"` FTable string `xml:"http://www.w3schools.com/furniture table,attr"`
Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"`
Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"`
Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"`
Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"`
} }
var tableAttrs = []struct { var tableAttrs = []struct {
...@@ -514,33 +519,33 @@ var tableAttrs = []struct { ...@@ -514,33 +519,33 @@ var tableAttrs = []struct {
xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` + xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
`h:table="hello" f:table="world" ` + `h:table="hello" f:table="world" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"hello", "world"}}, tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
}, },
{ {
xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` + xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
`h:table="hello" f:table="world" ` + `h:table="hello" f:table="world" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"hello", "world"}}, tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
}, },
{ {
xml: `<TableAttrs><TAttr ` + xml: `<TableAttrs><TAttr ` +
`h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` + `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"hello", "world"}}, tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}},
}, },
{ {
// Default space does not apply to attribute names. // Default space does not apply to attribute names.
xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` + xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
`h:table="hello" table="world" ` + `h:table="hello" table="world" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"hello", ""}}, tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
}, },
{ {
// Default space does not apply to attribute names. // Default space does not apply to attribute names.
xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` + xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` +
`table="hello" f:table="world" ` + `table="hello" f:table="world" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"", "world"}}, tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
}, },
{ {
xml: `<TableAttrs><TAttr ` + xml: `<TableAttrs><TAttr ` +
...@@ -553,7 +558,7 @@ var tableAttrs = []struct { ...@@ -553,7 +558,7 @@ var tableAttrs = []struct {
xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` + xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` +
`h:table="hello" table="world" ` + `h:table="hello" table="world" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"hello", ""}}, tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}},
ns: "http://www.w3schools.com/furniture", ns: "http://www.w3schools.com/furniture",
}, },
{ {
...@@ -561,7 +566,7 @@ var tableAttrs = []struct { ...@@ -561,7 +566,7 @@ var tableAttrs = []struct {
xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` + xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` +
`table="hello" f:table="world" ` + `table="hello" f:table="world" ` +
`/></TableAttrs>`, `/></TableAttrs>`,
tab: TableAttrs{TAttr{"", "world"}}, tab: TableAttrs{TAttr{HTable: "", FTable: "world"}},
ns: "http://www.w3.org/TR/html4/", ns: "http://www.w3.org/TR/html4/",
}, },
{ {
...@@ -596,14 +601,23 @@ func TestUnmarshalNSAttr(t *testing.T) { ...@@ -596,14 +601,23 @@ func TestUnmarshalNSAttr(t *testing.T) {
} }
func TestMarshalNSAttr(t *testing.T) { func TestMarshalNSAttr(t *testing.T) {
dst := TableAttrs{TAttr{"hello", "world"}} src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}}
data, err := Marshal(&dst) data, err := Marshal(&src)
if err != nil { if err != nil {
t.Fatalf("Marshal: %v", err) t.Fatalf("Marshal: %v", err)
} }
want := `<TableAttrs><TAttr xmlns:_1="http://www.w3.org/TR/html4/" _1:table="hello" xmlns:_2="http://www.w3schools.com/furniture" _2:table="world"></TAttr></TableAttrs>` want := `<TableAttrs><TAttr xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" xmlns:furniture="http://www.w3schools.com/furniture" furniture:table="world" xml:lang="en_US" xmlns:_xml="http://golang.org/xml/" _xml:other="other1" xmlns:_xmlfoo="http://golang.org/xmlfoo/" _xmlfoo:other="other2" xmlns:json="http://golang.org/json/" json:other="other3" xmlns:json_1="http://golang.org/2/json/" json_1:other="other4"></TAttr></TableAttrs>`
str := string(data) str := string(data)
if str != want { if str != want {
t.Errorf("have: %q\nwant: %q\n", str, want) t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want)
}
var dst TableAttrs
if err := Unmarshal(data, &dst); err != nil {
t.Errorf("Unmarshal: %v", err)
}
if dst != src {
t.Errorf("Unmarshal = %q, want %q", dst, src)
} }
} }
...@@ -273,6 +273,8 @@ func (d *Decoder) Token() (t Token, err error) { ...@@ -273,6 +273,8 @@ func (d *Decoder) Token() (t Token, err error) {
return return
} }
const xmlURL = "http://www.w3.org/XML/1998/namespace"
// Apply name space translation to name n. // Apply name space translation to name n.
// The default name space (for Space=="") // The default name space (for Space=="")
// applies only to element names, not to attribute names. // applies only to element names, not to attribute names.
...@@ -282,6 +284,8 @@ func (d *Decoder) translate(n *Name, isElementName bool) { ...@@ -282,6 +284,8 @@ func (d *Decoder) translate(n *Name, isElementName bool) {
return return
case n.Space == "" && !isElementName: case n.Space == "" && !isElementName:
return return
case n.Space == "xml":
n.Space = xmlURL
case n.Space == "" && n.Local == "xmlns": case n.Space == "" && n.Local == "xmlns":
return return
} }
......
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